Introduction.

This notebook reports on the inference of microbial association networks from meta-taxonomic data extracted from DairyFMBN using package NetCoMi, after some pre-processing carried out mostly with package phyloseq.

The objective of this analysis is:

  • infer microbial association networks (at the genus level) for selected cheese studies stored in DairyFMBN usign different inference methods

  • identify associations which are conserved across methods

  • compare different inference methods

  • identify and compare hubs in different networks

The data used in this script include studies from FMBN versions 3.2, with different platforms and target, but analyzed using the same pipeline based on DADA2. The script is designed to work with phyloseq objects extracted from FoodMicrobionet or DairyFMBN using the ShinyFMBN app DairyFMBN can be downloaded from Mendeley data: new versions are due soon.
The script will also work correctly with phyloseq objects generated using the DADA2 BioConductor pipeline but the use of SILVA as a taxonomic database is advisable. With some adaptations the script may work with phyloseq objects generated using other pipelines.

Analysis workflow.

  1. start from n (n>1) phyloseq objects. In this analysis I will use phyloseq objects extracted from DairyFMBN (which, in turn, includes all studies on dairy products stored in FoodMicrobionet). Although pooling at the genus level is likely to muddle some interactions, it it the only reasonable choice when comparing studies with vastly different targets, sequencing platforms, bioinformatic pilelines, etc.

  2. perform data-wrangling with phyloseq functions (mostly). There are a number of basic operations which should be performed:

    • remove samples with a low number of sequences (minseq) using the prune_samples() functions (and perhaps skip the analysis if there is less than min_samples). Note that sample and taxonomic filtering can be also performed within the NetCoMi::netConstruct() function

    • remove Eukaryota and Chloroplasts using subset_taxa()

    • remove taxa with ambiguous identification or poor identification (say at the domain or phylum level)

    • perform prevalence and abundance filtering (prevalence above min_prev AND abundance above min_rel_ab), possibly using filter_taxa()

    • return some sort of summary for what has been done and the effect on the original table (in terms of loss of taxa, sequences, samples), including some measure of diversity and evenness

  3. perform microbial association network inference with netConstruct() with some error trapping and store results in a list. This example uses the following inference methods:

    • SparCC (sparcc() in SpiecEasi package): needs high number of taxa and sparse matrix

    • CCREPE (ccrepe package, it is the main approach used in CoNet)

    • SpiecEasi (SpiecEasi package)

    • SPRING (SPRING package)

  4. get nodes, edges and network stats: use NetCoMi::netAnalyze() to get an object with both node and network stats; print a summary for network stats; build a tidygraph object for further stats; build tidy data frames with relevant stats;

The script is esigned to save data and amny of the plots in a subfolder (here “netcompare_output”).

The analysis.

Setting the options.

A number of options pertaining to the analysis or rlated to save operations can be set in the chunk below and will be saved and printed.

$general_options
$general_options$ncores
[1] 4

$general_options$data_folder
[1] "input_data"

$general_options$source_folder
[1] "source"

$general_options$output_folder
[1] "netcompare_output"

$general_options$process_batch
[1] TRUE

$general_options$use_lookup
[1] TRUE

$general_options$out_filenames
[1] "MAN_FMBN_genus_3"

$general_options$gres
[1] 300

$general_options$gtype
[1] "tiff"


$methods_options
$methods_options$methods
[1] "spieceasi" "spring"    "sparcc"    "ccrepe"   

$methods_options$parameters
$methods_options$parameters$spieaceasi
$methods_options$parameters$spieaceasi$sparMethod
[1] "t-test"

$methods_options$parameters$spieaceasi$alpha
[1] 0.001

$methods_options$parameters$spieaceasi$measureParList
$methods_options$parameters$spieaceasi$measureParList$method
[1] "mb"

$methods_options$parameters$spieaceasi$measureParList$lambda.min.ratio
[1] 0.01

$methods_options$parameters$spieaceasi$measureParList$nlambda
[1] 20

$methods_options$parameters$spieaceasi$measureParList$pulsar.params
$methods_options$parameters$spieaceasi$measureParList$pulsar.params$rep.num
[1] 50



$methods_options$parameters$spieaceasi$normmethodPar
[1] "none"

$methods_options$parameters$spieaceasi$zeromethodPar
[1] "none"

$methods_options$parameters$spieaceasi$dissFuncPar
[1] "signed"

$methods_options$parameters$spieaceasi$verbosePar
[1] 1


$methods_options$parameters$spring
$methods_options$parameters$spring$sparMethod
[1] "t-test"

$methods_options$parameters$spring$alpha
[1] 0.001

$methods_options$parameters$spring$measureParList
$methods_options$parameters$spring$measureParList$nlambda
[1] 50

$methods_options$parameters$spring$measureParList$rep.num
[1] 50


$methods_options$parameters$spring$normmethodPar
[1] "none"

$methods_options$parameters$spring$zeromethodPar
[1] "none"

$methods_options$parameters$spring$dissFuncPar
[1] "signed"

$methods_options$parameters$spring$verbosePar
[1] 1


$methods_options$parameters$sparcc
$methods_options$parameters$sparcc$sparMethod
[1] "t-test"

$methods_options$parameters$sparcc$alpha
[1] 0.001

$methods_options$parameters$sparcc$measureParList
$methods_options$parameters$sparcc$measureParList$iter
[1] 100

$methods_options$parameters$sparcc$measureParList$inner_it
[1] 20

$methods_options$parameters$sparcc$measureParList$th
[1] 0.05


$methods_options$parameters$sparcc$normmethodPar
[1] "none"

$methods_options$parameters$sparcc$zeromethodPar
[1] "none"

$methods_options$parameters$sparcc$dissFuncPar
[1] "signed"

$methods_options$parameters$sparcc$verbosePar
[1] 1


$methods_options$parameters$ccrepe
$methods_options$parameters$ccrepe$sparMethod
[1] "t-test"

$methods_options$parameters$ccrepe$alpha
[1] 0.001

$methods_options$parameters$ccrepe$measureParList
NULL

$methods_options$parameters$ccrepe$normmethodPar
[1] "fractions"

$methods_options$parameters$ccrepe$zeromethodPar
[1] "none"

$methods_options$parameters$ccrepe$dissFuncPar
[1] "signed"

$methods_options$parameters$ccrepe$verbosePar
[1] 1




$filtering_options
$filtering_options$min_samples
[1] 20

$filtering_options$min_seqs
[1] 1000

$filtering_options$glom_taxa
[1] "genus"

$filtering_options$rmunchar_dp
[1] TRUE

$filtering_options$rmchlmit
[1] TRUE

$filtering_options$rmeuk
[1] TRUE

$filtering_options$rmunchar_cof
[1] TRUE

$filtering_options$filter_OTUs
[1] TRUE

$filtering_options$prevfilter
[1] TRUE

$filtering_options$prevthreshold
[1] 0.05

$filtering_options$abthreshols
[1] 0.005

$filtering_options$passboth
[1] TRUE

$filtering_options$saveprevabplot
[1] TRUE

$filtering_options$printprevabplot
[1] FALSE

$filtering_options$saveprevabtable
[1] TRUE

$filtering_options$save_prev_ab_list
[1] TRUE


$netstat_options
$netstat_options$doVenn
[1] TRUE

$netstat_options$calcEbetw
[1] TRUE

$netstat_options$mergeNstats
[1] TRUE

Loading the objects to process.

The input data are stored in a folder (input_data) and processed one by one (depending on user input) or in batch (see process_batch option). The folder should also contain a .tsv with study metadata, with a label variable with the number of the study and an indicator on the nature of the file (FMBN or, if the accn number is in the name, ASV). Here I am loading 5 studies from DairyFMBN (ST106, ST110, ST115, ST131, ST136). The studies differ in the targets (ST106 targets V1-V3 RNA, the others the 16S RNA gene, V4 or V3-V4) and platform. Look at the example files to see how the study_metadata file should be setup.


── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  read_length_bp = col_double(),
  samples = col_double()
)
ℹ Use `spec()` for the full column specifications.
loading phyloseq objects: 1.459 sec elapsed

Filtering.

Filtering optionally filters samples and taxa (on the basis of taxonomy and of a prevalence and abundance filter) and returns the filtered phyloseq objects in a separate list. Taxonomic agglomeration is optionally carried out. The options for filtering are those defined in the options section.

Filter samples.

Samples with less than a given number of sequences are discarded first. As a consequence, a given phyloseq object may fall out below the set minimum of samples. Therefore the number of samples per object is checked again.

if(keep_time) tic("filter and glom samples")
# first prune objects with less than min_seqs sequences
physeq_list_0  <- physeq_list
# create first step of the filtering report
filtering_report <- vector("list", length = length(physeq_list))
filtering_report_0 <- map(physeq_list, report_step_0)
for(i in seq_along(physeq_list)){
  cat("processing ", i, " of ", length(physeq_list), "\n")
  # set plot_ecdf=F for faster execution
  physeq_list_0[[i]] <- prune_samples_by_size(physeq_list[[i]],
                                              names(physeq_list)[i], 
                                              plot_ecdf = F, 
                                              minseqs = min_seqs)
}
processing  1  of  5 
processing  2  of  5 
processing  3  of  5 
processing  4  of  5 
processing  5  of  5 
cat("...done pruning samples...\n")
...done pruning samples...
# now recheck if all objects have enough samples
physeq_list_1 <- rem_low_sample_obj(physeq_list_0, ms = min_samples)
# create second step of the filtering report
stage_name = "prune samples"
for(i in seq_along(physeq_list_0)){
  if(names(physeq_list_0)[i] %in% names(physeq_list_1)){
    name <- names(physeq_list_0)[i]
    stage <- report_step_n(my_physeq = physeq_list_1[[name]], 
                           my_physeq_o = physeq_list_0[[i]], 
                           stage_name = stage_name) 
  } else {
    stage <- c(stage_name,
              samples = NA_real_,
              sequences = NA_real_,
              taxa = NA_real_,
              prop_samples = NA_real_,
              prop_seq = NA_real_,
              prop_taxa = NA_real_)
  }
  filtering_report[[i]] <- rbind(stage_0 = as.character(filtering_report_0[[i]]), stage_1 =stage)
  names(filtering_report)[i] <- names(physeq_list_0)[i]
}


if(verbose_output) print(filtering_report)
$ST106_FMBN_ps
        stage           samples sequences taxa prop_samples prop_seq prop_taxa
stage_0 "original"      "30"    "128735"  "56" "1"          "1"      "1"      
stage_1 "prune samples" "30"    "128735"  "56" "1"          "1"      "1"      

$ST110_FMBN_ps
        stage           samples sequences taxa prop_samples prop_seq prop_taxa
stage_0 "original"      "112"   "2056840" "90" "1"          "1"      "1"      
stage_1 "prune samples" "112"   "2056840" "90" "1"          "1"      "1"      

$ST115_FMBN_ps
        stage           samples sequences taxa prop_samples prop_seq prop_taxa
stage_0 "original"      "63"    "2346838" "45" "1"          "1"      "1"      
stage_1 "prune samples" "63"    "2346838" "45" "1"          "1"      "1"      

$ST131_FMBN_ps
        stage           samples sequences taxa  prop_samples prop_seq prop_taxa
stage_0 "original"      "108"   "734976"  "302" "1"          "1"      "1"      
stage_1 "prune samples" "107"   "734493"  "302" "1"          "1"      "1"      

$ST136_FMBN_ps
        stage           samples sequences taxa prop_samples prop_seq prop_taxa
stage_0 "original"      "47"    "2759593" "60" "1"          "1"      "1"      
stage_1 "prune samples" "47"    "2759593" "60" "1"          "1"      "1"      
if(play_audio) beep(sound = 6)
if(keep_time) toc()
filter and glom samples: 0.513 sec elapsed

Taxonomic filtering.

A number of taxonomic filtering operations is performed at this stage (depending on the nature of the object and filtering options, set in the setting_options chunk). Plots are optionally produced and saved.

if(keep_time) tic("taxonomic filtering, step 1")

# check the nature of the taxonomic table
# in FMBN you either have ASV tables or tax tables with agglomeration at the species level or above
# which are the names of the tax levels?
tax_levels <- c("domain","phylum","class","order","family","genus","species")
# get/set rank names (this is necessary because phyloseq objects from FMBN or from the
# bioconductor pipeline with SILVA have different names)

physeq_list_2 <- lapply(physeq_list_1, gset_rank_names, tax_levels = tax_levels)
no change in rank names necessary
no change in rank names necessary
no change in rank names necessary
no change in rank names necessary
no change in rank names necessary
# remove uncharacterized taxa in all phyloseq objects (using a functional)
physeq_list_3 <- physeq_list_2
if(rm_unchar){
  physeq_list_3 <- lapply(physeq_list_2, subset_taxa, !is.na(domain) & !domain %in% c("", "uncharacterized"))
  physeq_list_3 <- lapply(physeq_list_3, subset_taxa, !is.na(phylum) & !phylum %in% c("", "uncharacterized"))
}

# optionally remove Eukaryotes
if(rm_euk) {
  physeq_list_3 <- lapply(physeq_list_3, subset_taxa, domain !="Eukaryota")
}

# optionally remove chloroplasts and mitochondria 
if(rm_chlmit) {
  physeq_list_3 <- lapply(physeq_list_3, remove_Chl_Mit)
}

# use lookup table to change taxonomy of Lactobacillus; necessary for phyloseq objects produced with SILVA
# but not for objects extracted from FMBN (which are transformed before extraction); will also change the 
# taxa names for ASVs
# MAY BE SLOW
if(use_lookup){
  # loop over the list of phyloseq objects
  for(i in seq_along(physeq_list_3)){
    # check the length of the names of the taxa; if <100 it is from FMBN, break
    if(mean(sapply(taxa_names(physeq_list_3[[i]]), nchar),na.rm = T)<100){
      next
    } else {
      # change the names
      tnames <- str_c("ASV",seq(1:ntaxa(physeq_list_3[[i]])))
      taxa_names(physeq_list_3[[i]])<-tnames
      # get species to change
      taxa_table <- as.data.frame(as(tax_table(physeq_list_3[[i]]),"matrix"))
      taxa_table <- taxa_table %>% mutate(id = str_c(genus, species, sep = " "))
      taxa_table <- left_join(taxa_table, lookup_table)
      n_changes <- sum(!is.na(taxa_table$new_species))
      taxa_table <- taxa_table %>% mutate(species = if_else(!is.na(new_species), new_species, species),
                                          genus = if_else(!is.na(new_genus), new_genus, genus)
      )
      # remove columns
      taxa_table <- dplyr::select(taxa_table, domain:species)
      # now change genus for Lactobacillus with no species
      to_change_lb <- which((taxa_table$genus == "Lactobacillus") & is.na(taxa_table$species))
      taxa_table$genus[to_change_lb]<-"Lactobacillus complex"
      # replace Leuconostocaceae with Lactobacillaceae
      n_leuc <- nrow(dplyr::filter(taxa_table, family == "Leuconostocaceae"))
      taxa_table <- taxa_table %>% mutate(family = ifelse(family == "Leuconostocaceae", "Lactobacillaceae", family))
      taxa_table <- as.matrix(taxa_table)
      rownames(taxa_table)<-tnames
      tax_table(physeq_list_3[[i]])<-taxa_table
      if(verbose_output){
        cat(names(physeq_list_3)[i],": changed ", n_changes+n_leuc, " taxa\n", sep ="")
      }
    }
  }
}

# remove further taxa which are uncharacterized at the family to class level

# note for self: might improve it by setting the level at or above which uncharacterized taxa can be removed
# rather than using a T/F flag
if(above_genus_flag){
  physeq_list_3 <- lapply(physeq_list_3, subset_taxa, !is.na(class) & !class %in% c("", "uncharacterized")) # Class
  physeq_list_3 <- lapply(physeq_list_3, subset_taxa, !is.na(order) & !order %in% c("", "uncharacterized")) # Order
  physeq_list_3 <- lapply(physeq_list_3, subset_taxa, !is.na(family) & !family %in% c("", "uncharacterized")) # Family
}

# optionally perform taxonomic agglomeration, 

# may take some time; can be made faster with plyr functions using parallelization or with furrr
physeq_list_4 <- physeq_list_3
if(taxglom != "none"){
  physeq_list_4 <- lapply(physeq_list_3, tax_glom_name_change, taxa_glom = taxglom)
}

# create third step of the filtering report
stage_name = "taxonomic filter+glom"
for(i in seq_along(physeq_list_0)){
  if(names(physeq_list_0)[i] %in% names(physeq_list_1)){
    name <- names(physeq_list_0)[i]
    stage <- report_step_n(my_physeq = physeq_list_4[[name]], 
                           my_physeq_o = physeq_list_0[[i]], 
                           stage_name = stage_name) 
  } else {
    stage <- c(stage_name,
              samples = NA_real_,
              sequences = NA_real_,
              taxa = NA_real_,
              prop_samples = NA_real_,
              prop_seq = NA_real_,
              prop_taxa = NA_real_)
  }
  filtering_report[[i]] <- rbind(filtering_report[[i]], stage_3 =stage)
}

if(keep_time) toc()
taxonomic filtering, step 1: 11.171 sec elapsed
if(keep_time) tic("calculate diversity pre-filter")

# Calculate diversity prior to filtering for prevalence and abundance

div_est_prefilter <- map_dfr(physeq_list_4, phyloseq::estimate_richness, split = F, measure=c("Observed","Chao1","Shannon"))
The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.
div_est_prefilter <- mutate(div_est_prefilter, Pielou_J = Shannon/log(Observed))
# calculate and add ave Bray-Curtis dissimilarity
meanbcdist <- map(physeq_list_4, phyloseq::distance, method="bray")
div_est_prefilter$ave_BC <- unlist(map(meanbcdist, mean))

row.names(div_est_prefilter) <- names(physeq_list_4)
# save for further use
save(div_est_prefilter, 
     file = paste(file.path(output_folder,out_filename_pref), "_divprefilter.Rdata",sep=""))

if(keep_time) toc()
calculate diversity pre-filter: 0.201 sec elapsed
# Prevalence and abundance filter
if(keep_time) tic("taxonomic filtering, step 2")

# NOTE using a prevalence filter based on fraction may be wrong for studies with large 
# number of samples, in which one might want to retain taxa which appear in >10 samples 

physeq_list_5 <- physeq_list_4
# will be skipped if filterOTUs == F
node_stat_list <- vector("list", length = length(physeq_list_5))
# a list which will host node stats, like the prevab data, initially empty,
# if nothing is added at this stage, node stats will be added at a later stage
if(filterOTUs){
  prev_ab_list <- vector("list", length = length(physeq_list_4))
  # will host the lists with the results
  for(i in seq_along(physeq_list_4)){
    if(verbose_output) cat("prevalence and abundance filter, physeq ",i," of ",
                           length(physeq_list_4),"\n")
    prev_ab_list[[i]] <- filter_by_prev_ab(
      myphyseq = physeq_list_4[[i]],
      name = names(physeq_list_4)[i]
    )
    names(prev_ab_list)[i]<-names(physeq_list_4)[i]
    # save the processed phyloseq
    physeq_list_5[[i]] <- prev_ab_list[[i]][[1]]
    # save prev ab table
    node_stat_list[[i]] <- prev_ab_list[[i]][[4]]
    names(node_stat_list)[i] <- names(physeq_list_4)[i]
  }
}
prevalence and abundance filter, physeq  1  of  5 
Joining, by = "label"
Saving 7 x 7 in image
prevalence and abundance filter, physeq  2  of  5 
prevalence and abundance filter, physeq  3  of  5 
prevalence and abundance filter, physeq  4  of  5 
prevalence and abundance filter, physeq  5  of  5 
# optionally save the prev_ab_list
if(save_prev_ab_list) save(prev_ab_list, 
                           file = paste(file.path(output_folder,out_filename_pref), name, "_prevabl.Rdata",sep=""))

# create fourth step of the filtering report
stage_name = "prevalence and abundance"
for(i in seq_along(physeq_list_0)){
  if(names(physeq_list_0)[i] %in% names(physeq_list_1)){
    name <- names(physeq_list_0)[i]
    stage <- report_step_n(my_physeq = physeq_list_5[[name]], 
                           my_physeq_o = physeq_list_0[[i]], 
                           stage_name = stage_name) 
  } else {
    stage <- c(stage_name,
              samples = NA_real_,
              sequences = NA_real_,
              taxa = NA_real_,
              prop_samples = NA_real_,
              prop_seq = NA_real_,
              prop_taxa = NA_real_)
  }
  filtering_report[[i]] <- rbind(filtering_report[[i]], stage_4 =stage)
}

# remove unneeded objects
rm(physeq_list_1, physeq_list_3, physeq_list_4, prev_ab_list, taxa_table)
# create a data frame with the report
filtering_report_df <- map_dfr(filtering_report, as.data.frame, .id = "dataset")
filtering_report_df <- filtering_report_df %>% 
  mutate(data_type = if_else(str_detect(dataset, "FMBN"), "FMBN", "ASV")) %>%
  mutate(dplyr::across(.cols = samples:prop_taxa, as.numeric)) %>%
  mutate(stage = as_factor(stage))
if(verbose_output) {
  print(filtering_report_df)
  print(filtering_report_df %>%
          dplyr::filter(stage != "original" & stage != "prune samples") %>%
          ggplot(mapping = aes(x = data_type, y = prop_taxa)) +
          facet_wrap(~stage) +
          geom_boxplot() +
          labs(x = "data type", y = "prop. taxa left"))
  summ_filtering <- filtering_report_df %>%
    group_by(data_type, stage) %>%
    summarize(min_seq = min(prop_seq), 
              max_seq = max(prop_seq),
              min_taxa = min(prop_taxa),
              max_taxa = max(prop_taxa))
  print(summ_filtering)
}

if(play_audio) beep(sound = 6)
if(keep_time) toc()
taxonomic filtering, step 2: 4.43 sec elapsed

The reduction in number of “taxa” is dramatic in most cases (from 0.010 to 0.21 left), while between 0.984 and 0.999 of the sequences are left.

Inferring the networks.

I will now try Microbial Association Network inference, with the methods and parameters specified in the options chunk. Separate lists will be generated for each method.
An error trapping routine has been implemented: if MAN estimation fails a try-error object rather than an object of class microNet will be returned.
All the returned objects will be saved in a list (1 slot for each phyloseq object, each with 1 slot for each inference method).

if(keep_time) tic("Microbial association network inference")
if(verbose_output) cat("Please be patient, this will take a while...\n")
Please be patient, this will take a while...
# list for results, 1 slot for each object
MAN_inf_results <- vector("list", length = length(physeq_list_5))
# create list for methods, 1 slot for each method
inf_meth_list <- vector("list", length = length(inf_methods))

for(i in seq_along(physeq_list_5)){
  name <- names(physeq_list_5)[i]
  if(verbose_output) cat("\n", "Inferring network(s) for ", name, ", ", i, "of ", 
                         length(physeq_list_5), "\n", sep=" ")
  if(keep_time) {
    ticmessage_object <- paste("microbial association network inference, ",
                               name, ", ", i, " of ", length(physeq_list_5), sep = "")
    tic(ticmessage_object)
  }
  for(j in seq_along(inf_methods)){
    if(keep_time){
      ticmessage_method <- paste("\n", "inference with method ",
                                 inf_methods[j], ", ", j, " of ", length(inf_methods), sep = "")
      tic(ticmessage_method)
    }
    infmethod <- inf_methods[[j]]
    infparam <- inf_methods_param[[j]]
   
    # infrence is carried out here using a user defined function loaded with the `source()` command 
    # you do not need to provide much detail, everything is taken care of in the options
    # of course could be customized in the function call
    inf_meth_list[[j]] <- infer_MAN(myphyseq = physeq_list_5[[i]],
                                    inf_method = infmethod,
                                    method_parameters = infparam)
    names(inf_meth_list)[j] <- infmethod
    if(keep_time) toc()
  }
  MAN_inf_results[[i]] <- inf_meth_list
  names(MAN_inf_results)[i] <- name
  if(keep_time) toc()
}

 Inferring network(s) for  ST106_FMBN_ps ,  1 of  5 
Infos about changed arguments:
Sparsification included in 'spieceasi'.

12 taxa and 30 samples remaining.
Optimal lambda may be larger than the supplied values

inference with method spieceasi, 1 of 4: 108.972 sec elapsed
Infos about changed arguments:
Sparsification included in 'spring'.

12 taxa and 30 samples remaining.
1 job had warning: "There are variables in the data that have only zeros or only the same values."Optimal lambda may be larger than the supplied values

inference with method spring, 2 of 4: 69.647 sec elapsed
12 taxa and 30 samples remaining.

inference with method sparcc, 3 of 4: 1.134 sec elapsed
12 taxa and 30 samples remaining.
Feature(s) Halomonas, Loigolactobacillus, Tetragenococcus have more zeros than the threshold of 22 zeros.  Excluding from output (will still be used in normalizing.)All-zero subjects generated by permutation: 16.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 5.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 14.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 12.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 5.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 28.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 7.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 28.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 28.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 17.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 21.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 29.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 19.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 10.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 8.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 14.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 8.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 7.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 26.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 4.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 26.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 10.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 7.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 7.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 1.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 25.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 14.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 9.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 4.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 1.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 17.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 17.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 30.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 18.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 9.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 30.  These will be replaced by all zeros after normalizationAll-zero subjects generated by permutation: 1.  These will be replaced by all zeros after normalizationAssociation matrix contains NAs (replaced by zeros).

inference with method ccrepe, 4 of 4: 0.824 sec elapsed
microbial association network inference, ST106_FMBN_ps, 1 of 5: 180.579 sec elapsed

 Inferring network(s) for  ST110_FMBN_ps ,  2 of  5 
Infos about changed arguments:
Sparsification included in 'spieceasi'.

14 taxa and 112 samples remaining.

inference with method spieceasi, 1 of 4: 101.319 sec elapsed
Infos about changed arguments:
Sparsification included in 'spring'.

14 taxa and 112 samples remaining.

inference with method spring, 2 of 4: 62.308 sec elapsed
14 taxa and 112 samples remaining.

inference with method sparcc, 3 of 4: 1.378 sec elapsed
14 taxa and 112 samples remaining.

inference with method ccrepe, 4 of 4: 2.778 sec elapsed
microbial association network inference, ST110_FMBN_ps, 2 of 5: 167.786 sec elapsed

 Inferring network(s) for  ST115_FMBN_ps ,  3 of  5 
Infos about changed arguments:
Sparsification included in 'spieceasi'.

8 taxa and 63 samples remaining.
Optimal lambda may be larger than the supplied values

inference with method spieceasi, 1 of 4: 104.691 sec elapsed
Infos about changed arguments:
Sparsification included in 'spring'.

8 taxa and 63 samples remaining.
Optimal lambda may be larger than the supplied values

inference with method spring, 2 of 4: 61.183 sec elapsed
8 taxa and 63 samples remaining.

inference with method sparcc, 3 of 4: 0.992 sec elapsed
8 taxa and 63 samples remaining.
Feature(s) Staphylococcus have more zeros than the threshold of 54 zeros.  Excluding from output (will still be used in normalizing.)Association matrix contains NAs (replaced by zeros).

inference with method ccrepe, 4 of 4: 0.526 sec elapsed
microbial association network inference, ST115_FMBN_ps, 3 of 5: 167.395 sec elapsed

 Inferring network(s) for  ST131_FMBN_ps ,  4 of  5 
Infos about changed arguments:
Sparsification included in 'spieceasi'.

3 taxa and 107 samples remaining.
Optimal lambda may be larger than the supplied values

inference with method spieceasi, 1 of 4: 104.719 sec elapsed
Infos about changed arguments:
Sparsification included in 'spring'.

3 taxa and 107 samples remaining.
Optimal lambda may be larger than the supplied values

inference with method spring, 2 of 4: 58.292 sec elapsed
3 taxa and 107 samples remaining.

inference with method sparcc, 3 of 4: 0.696 sec elapsed
3 taxa and 107 samples remaining.

inference with method ccrepe, 4 of 4: 0.431 sec elapsed
microbial association network inference, ST131_FMBN_ps, 4 of 5: 164.14 sec elapsed

 Inferring network(s) for  ST136_FMBN_ps ,  5 of  5 
Infos about changed arguments:
Sparsification included in 'spieceasi'.

11 taxa and 47 samples remaining.

inference with method spieceasi, 1 of 4: 103.196 sec elapsed
Infos about changed arguments:
Sparsification included in 'spring'.

11 taxa and 47 samples remaining.

inference with method spring, 2 of 4: 59.983 sec elapsed
11 taxa and 47 samples remaining.

inference with method sparcc, 3 of 4: 0.936 sec elapsed
11 taxa and 47 samples remaining.

inference with method ccrepe, 4 of 4: 0.74 sec elapsed
microbial association network inference, ST136_FMBN_ps, 5 of 5: 164.858 sec elapsed
# create a report
inference_report <- vector("list", length(MAN_inf_results))
for(i in seq_along(MAN_inf_results)){
  inference_report[[i]]<-map_dfr(MAN_inf_results[[i]], class, .id = "method")
  names(inference_report)[i]<-names(MAN_inf_results)[i]
}
inference_report_df <- bind_rows(inference_report, .id = "dataset")

# save the list and do some clean-up
save(MAN_inf_results, file = file.path(output_folder, paste(out_filename_pref,"_MANlist.Rdata")))
rm(inf_meth_list, inference_report)
if(play_audio) beep(sound = 6)
if(keep_time) toc()
Microbial association network inference: 845.192 sec elapsed

Analyze the networks.

The inferred networks will be analyzed using NetCoMi::netAnalyze() and the resulting objects will be processed further.
Network, node and edge stats will be extracted to data frames/tibbles for further processing.
A few extra analyses will be carried out using package phyloseq to add diversity and evenness indices to the metadata.


if(keep_time) tic("calculate diversity post-filter")

# estimate diversity for each object of the physeq_list_5, returns a list with the results
# extract and put together with metadata
# will generate warnings

div_est_postfilter <- map_dfr(physeq_list_5, estimate_richness, split = F, 
                              measures = c("Observed","Chao1","Shannon"), .id = "label")
The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.The data you have provided does not have
any singletons. This is highly suspicious. Results of richness
estimates (for example) are probably unreliable, or wrong, if you have already
trimmed low-abundance taxa from the data.

We recommended that you find the un-trimmed data and retry.
div_est_postfilter <- mutate(div_est_postfilter, Pielou_J = Shannon/log(Observed))

# calculate and add average Bray-Curtis dissimilarity
meanbcdist <- map(physeq_list_5, phyloseq::distance, method="bray")
div_est_postfilter$ave_BC <- unlist(map(meanbcdist, mean))

row.names(div_est_postfilter) <- names(physeq_list_5)
# save for further use
save(div_est_postfilter, 
     file = paste(file.path(output_folder,out_filename_pref), "_divpostfilter.Rdata",sep=""))

# an alternative could be to use it on div_est_prefilter
study_metadata <- left_join(study_metadata, div_est_postfilter)
Joining, by = "label"
if(keep_time) toc()
calculate diversity post-filter: 0.233 sec elapsed
# calculate network statistics with netAnalyze
if(keep_time) tic("Calculate network statistics")

# the list for the methods within the dataset
net_stats <- vector("list", length = length(MAN_inf_results))
# net_stat_results is a list, do the calculation for each of the datasets, all inference methods
for(i in seq_along(MAN_inf_results)){
  name <- names(MAN_inf_results)[i]
  if(verbose_output) cat("Calculating network stats for",name,"\n")
  net_stat_results <- vector("list", length = length(MAN_inf_results[[i]]))
  for(j in seq_along(MAN_inf_results[[i]])){
    mthd <- names(MAN_inf_results[[i]])[j]
    if(verbose_output) cat("method",mthd,"\n")
    # consider reducing argument hubQuant to 0.75-0.90 default is 0.95),
    net_stat_results[[j]] <- calculate_net_stats(MAN_inf_results[[i]][[j]])
  }
  names(net_stat_results)<-names(MAN_inf_results[[i]])
  net_stats[[i]] <- net_stat_results
  names(net_stats)[i]<-names(MAN_inf_results)[i]
}
Calculating network stats for ST106_FMBN_ps 
method spieceasi 
method spring 
Error in netAnalyze(microNet_obj, centrLCC = TRUE, avDissIgnoreInf = FALSE,  : 
  Network is empty.
method sparcc 
method ccrepe 
Calculating network stats for ST110_FMBN_ps 
method spieceasi 
method spring 
method sparcc 
method ccrepe 
Calculating network stats for ST115_FMBN_ps 
method spieceasi 
method spring 
Error in netAnalyze(microNet_obj, centrLCC = TRUE, avDissIgnoreInf = FALSE,  : 
  Network is empty.
method sparcc 
method ccrepe 
Calculating network stats for ST131_FMBN_ps 
method spieceasi 
method spring 
Error in netAnalyze(microNet_obj, centrLCC = TRUE, avDissIgnoreInf = FALSE,  : 
  Network is empty.
method sparcc 
method ccrepe 
Weighted degree used (unweighted degree not meaningful for a fully connected network).
Argument 'normDeg' set to FALSE
(no normalization implemented for weighted degree).
Calculating network stats for ST136_FMBN_ps 
method spieceasi 
method spring 
method sparcc 
method ccrepe 
rm(net_stat_results, meanbcdist, mthd)
if(keep_time) toc()
Calculate network statistics: 0.478 sec elapsed
# extracting global network properties

if(keep_time) tic("Extraction global network properties")
# extracting the global network stats 
global_props_list_a <-vector("list",length(net_stats))
global_props_list_l <-vector("list",length(net_stats)) 
global_props_list_all <-vector("list",length(inf_methods))
global_props_list_lcc <-vector("list",length(inf_methods))
for (i in seq_along(net_stats)){
  dataset <- names(net_stats)[i]
  for (j in seq_along(net_stats[[i]])){
    if(class(net_stats[[i]][[j]])!= "microNetProps"){
      next
    } else{
      nnodes <- sum(net_stats[[i]][[j]]$centralities$degree1>0)
      ntaxa <- nrow(net_stats[[i]][[j]]$input$assoMat1)
      nposedge <- sum(net_stats[[i]][[j]]$input$assoMat1[lower.tri(net_stats[[i]][[j]]$input$assoMat1)]>0)
      nnegedge <- sum(net_stats[[i]][[j]]$input$assoMat1[lower.tri(net_stats[[i]][[j]]$input$assoMat1)]<0)
      extra_prop_vector <- c(nnodes, ntaxa, nposedge, nnegedge)
      names(extra_prop_vector) <- c("nnodes", "ntaxa", "nposedge", "nnegedge")
      global_props_list_all[[j]] <- c(unlist(net_stats[[i]][[j]]$globalProps),extra_prop_vector)
      global_props_list_lcc[[j]] <- c(unlist(net_stats[[i]][[j]]$globalPropsLCC),extra_prop_vector)
      names(global_props_list_all)[j] <- names(global_props_list_lcc)[j]<- names(net_stats[[i]][j])
    }
    # create data frame with results
    all_df <- bind_rows(global_props_list_all, .id = "method")
    lcc_df <- bind_rows(global_props_list_lcc, .id = "method") 
  }
  global_props_list_a[[i]] <- all_df
  global_props_list_l[[i]] <- lcc_df
  names(global_props_list_a)[i] <- names(global_props_list_l)[i] <- names(net_stats)[i]
}
# note that str_sub only works if you have <=9 inference methods in inf_methods
global_all_df <- bind_rows(global_props_list_a, .id = "dataset") 
global_lcc_df <- bind_rows(global_props_list_l, .id = "dataset") 


global_all_df <- left_join(global_all_df, 
                           select(study_metadata,label, obj_type, target, 
                                  region, platform, samples, type, Observed, 
                                  Chao1, Shannon, Pielou_J, ave_BC), 
                           by = c("dataset" = "label")) %>%
  mutate(across(where(is.numeric), ~na_if(.,Inf)))
global_lcc_df <- left_join(global_lcc_df, 
                           select(study_metadata,label, obj_type, target, 
                                  region, platform, samples, type, Observed, 
                                  Chao1, Shannon, Pielou_J, ave_BC), 
                           by = c("dataset" = "label")) %>%
  mutate(across(where(is.numeric), ~na_if(.,Inf)))

# both might contain Inf which are replaced by NA (using dplyr::na_if()) to be handled
# correctly if doing PCA by pairwise deletion.
# the problem only occurs in avPath1 and clustCoef1, but I am handling it with a scoped mutate
# a better solution might be the use of hablar::rationalize()


# save the data frames for further use
write_tsv(global_all_df, file = paste(file.path(output_folder,out_filename_pref), "_netpropall.txt",sep=""))
write_tsv(global_lcc_df, file = paste(file.path(output_folder,out_filename_pref), "_netproplcc.txt",sep=""))

# print a summary table
global_all_df

rm(global_props_list_a, global_props_list_l, global_props_list_all, 
   global_props_list_lcc, all_df, lcc_df, nnodes, ntaxa, nposedge, nnegedge, extra_prop_vector)
if(play_audio) beep(sound = 6)
if(keep_time) toc()
Extraction global network properties: 0.744 sec elapsed

Global network properties have been saved as data frames for further use. They will be used together those generated for the other data sets.

Node properties.

Node properties can be extracted from the microNetProps objects and saved for further use.


# extract node properties
# using a loop takes slightly longer that using functionals but handles names better
# note that when using ASVs comparing nodes between datasets does not make much sense

if(keep_time) tic("Extracting node properties")

node_stats <- vector("list", length = length(net_stats))
for (i in seq_along(net_stats)) {
  node_properties <- node_stat_list[[i]]
  
  if(verbose_output) cat("extracting node stats for", names(net_stats)[i],"\n")
  for (j in seq_along(net_stats[[i]])) {
    if (class(net_stats[[i]][[j]]) == "microNetProps") {
      node_stats[[i]][[j]] <- extract_node_stats(net_stat_list = net_stats[[i]][[j]], 
                                                 nodestat = node_properties)
      method <- names(net_stats[[i]])[j]
      dataset <- names(net_stats)[i]
      nrows <- nrow(node_stats[[i]][[j]])
      node_stats[[i]][[j]] <- bind_cols(
        dataset = rep(dataset,nrows),
        method = rep(method,nrows),
        node_stats[[i]][[j]]
        )
    } else {
      cat("no node stats to return for",
          names(net_stats)[i],
          names(node_stats[[i]])[j],
          "\n")
      next
    }
  }
  node_stats[[i]]<-bind_rows(node_stats[[i]])
}
extracting node stats for ST106_FMBN_ps 
Joining, by = "label"
no node stats to return for ST106_FMBN_ps 
Joining, by = "label"
Joining, by = "label"
extracting node stats for ST110_FMBN_ps 
Joining, by = "label"
Joining, by = "label"
Joining, by = "label"
Joining, by = "label"
extracting node stats for ST115_FMBN_ps 
Joining, by = "label"
no node stats to return for ST115_FMBN_ps 
Joining, by = "label"
Joining, by = "label"
extracting node stats for ST131_FMBN_ps 
Joining, by = "label"
no node stats to return for ST131_FMBN_ps 
Joining, by = "label"
Joining, by = "label"
extracting node stats for ST136_FMBN_ps 
Joining, by = "label"
Joining, by = "label"
Joining, by = "label"
Joining, by = "label"
node_stats_df <- bind_rows(node_stats)
# perform some tidying
node_stats_df <- node_stats_df %>%
  tidyr::separate(dataset, into = c("Study", "Accn_n", "suf"), sep = "_", remove = F) %>% 
  dplyr::select(-Accn_n, -suf) %>%
  mutate(label2 = if_else(!str_detect(dataset, "FMBN"),str_c(label, Study, sep = "_"), label))

# label2 is only necessary when using ASVs or OTUs, not if there has been taxonomic agglomeration
# consider removing the mutate instruction

write_tsv(node_stats_df, file = paste(file.path(output_folder,out_filename_pref), "_nodestats_df.txt",sep=""))
rm(node_stats)
if(play_audio) beep(sound = 6)
if(keep_time) toc()
Extracting node properties: 0.725 sec elapsed

Edge properties.

Edges and edge properties can also be extracted for further use. Important edge properties are:

  • edge type (positive or negative)

  • edge weight and association measure

  • edge betweenness

It is also convenient to compare edges among different graphs (and to do so it might be convenient to make sure that “to” and “from” are always in alphabetical order, which is always true within the same graph but might not be necessarily true among different graphs).
The following chunk will (optionally):

  • integrate node (calculated with netAnalyse()) in the tidygraph list,

  • calculate edge betweenness,

  • compare networks (within dataset) by producing and saving Venn diagrams of the edges

Finally, a data frame with all edges will be produced for further use.


if(keep_time) tic("List with tidygraph objects created")

# creating a tidygraph object for each net
tidygraph_list <- vector("list", length = length(MAN_inf_results))

for(i in seq_along(MAN_inf_results)){
  if(verbose_output) cat("Converting in tidygraphs for", names(MAN_inf_results)[i],"\n")
  # the warning, if any, is not very informative, should consider passing names of datasets and methods
  tidygraph_list[[i]] <- MAN_inf_results[[i]] %>% map(microNet_to_tidygraph, fail_w_err = F, use_asso_matrix = T)
}
Converting in tidygraphs for ST106_FMBN_ps 
Joining, by = c("from", "to")
the network has 0 edges
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Converting in tidygraphs for ST110_FMBN_ps 
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Converting in tidygraphs for ST115_FMBN_ps 
Joining, by = c("from", "to")
the network has 0 edges
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Converting in tidygraphs for ST131_FMBN_ps 
Joining, by = c("from", "to")
the network has 0 edges
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Converting in tidygraphs for ST136_FMBN_ps 
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Joining, by = c("from", "to")
Joining, by = c("from", "to")
names(tidygraph_list)<-names(MAN_inf_results)
if(keep_time) toc()
List with tidygraph objects created: 1.213 sec elapsed
# optionally merge further node stats (depends on merge_n_stats) 
# and calculate edge betweenness (depends on calc_e_betw)
# I am using a loop
if(keep_time) tic("Stats added to tidygraphs, edge dataframe created")
tidygraph_list_wstats <- vector("list", length = length(tidygraph_list))
# the list with the edge data frames
edge_list <- vector("list", length = length(tidygraph_list))

for(i in seq_along(tidygraph_list_wstats)){
  # need to be reset
  inner_tgl <- vector("list", length = length(tidygraph_list[[i]]))
  inner_el <- vector("list", length = length(tidygraph_list[[i]]))
  dtst <- names(tidygraph_list)[i]
  for(j in seq_along(inner_tgl)){
    if(is.tbl_graph(tidygraph_list[[i]][[j]])){
    mthd = names(tidygraph_list[[i]])[j]
    nstats <- node_stats_df %>% dplyr::filter(dataset == dtst & method == mthd)
    inner_tgl[[j]] <- merge_stats(tg = tidygraph_list[[i]][[j]],
                             node_stats = nstats, 
                             ebetw = calc_e_betw)
     # extract edge tibble
    inner_el[[j]] <- inner_tgl[[j]] %>% 
      activate(edges) %>% 
      as_tibble() %>%
      mutate(method = mthd)
    # do naming
    names(inner_tgl)[j] <- names(inner_el)[j] <- names(tidygraph_list[[i]])[j]
    } else {
      next
    }
  }
  tidygraph_list_wstats[[i]] <- inner_tgl
  edge_list[[i]] <- bind_rows(inner_el)
  edge_list[[i]] <- edge_list[[i]] %>% 
    mutate(dataset = dtst) %>% dplyr::select(dataset, method, !(dataset:method))
  names(tidygraph_list_wstats)[i] <- names(edge_list)[i] <- names(tidygraph_list)[i]
}
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
Joining, by = "name"
rm(nstats)
# build and fix the edge df
edge_list_df <- bind_rows(edge_list)

edge_list_df <- edge_list_df %>% 
  mutate(name_from_sorted = if_else(from_name < to_name, from_name, to_name),
         name_to_sorted = if_else(from_name < to_name, to_name, from_name)) %>%
  mutate(edge_name = str_c(name_from_sorted, name_to_sorted, sep = "--"))
write_tsv(edge_list_df, file = paste(file.path(output_folder,out_filename_pref), "_edgelist_df.txt",sep=""))
if(keep_time) toc()
Stats added to tidygraphs, edge dataframe created: 1.264 sec elapsed
# make Venn plots

# I am using a loop
if(do_Venn){
  if(keep_time) tic("Venn plots created and saved")
  Venn_list <- vector("list", length(tidygraph_list))
  for(i in seq_along(tidygraph_list)){
    # need to select only elements of class tbl_graph
    tblgrphs <- map_lgl(tidygraph_list[[i]], is.tbl_graph)
    names(Venn_list) <- names(tidygraph_list)
    if(length(tidygraph_list[[i]][tblgrphs])>1){
      inner_list <- map(tidygraph_list[[i]][tblgrphs], function(x) as_ids(E(x)))
      Venn_title <- names(tidygraph_list)[i]
      Venn_file <- paste(file.path(output_folder,out_filename_pref),"_",Venn_title, "_venns.tiff",sep="")
      my_fill <- (2:5)[1:length(tidygraph_list[[i]][tblgrphs])]
      Venn_list[[i]] <- venn.diagram(inner_list, 
                                       fill = my_fill, 
                                       alpha = 0.3, 
                                       filename = Venn_file, 
                                       margin = 0.05, 
                                       main = Venn_title, 
                                       main.cex = 1.5, 
                                       main.fontface = "bold", 
                                       main.fontfamily = "sans", 
                                       main.pos = c(0.5, 1.05)
        )
    }
  }
  rm(inner_list, Venn_title, Venn_file)
  if(keep_time) toc()
}
Venn plots created and saved: 1.841 sec elapsed
if(play_audio) beep(sound = 6)

Looking at the Venns it can be seen that the sparsest networks are almost always produced by SPIEC-EASI and that the number of edges shared by all methods for any given is very low.

Comparing global properties.

Comparing global network properties may be of assistance in evaluating the effect of the inference method or of the type of study. Although NetCoMi offers very effective tools to compare two networks, it does not easily generalize to more than 2-3 networks. Here, I will use the global_all_df and global_lcc_df and produce graphs using PCA.
Note line 1051 should be manually adapted to show all loadings and scores

if(keep_time) tic("Comparing global properties")
# create a label and extract matrix for the analysis
global_all_df_2 <- global_all_df %>%
  tidyr::separate(dataset, into = c("study", "type", "object"), remove = F) %>%
  select(-type, -object) %>%
  mutate(method_brief = case_when(
    method == "spieceasi" ~ "spi",
    method == "spring" ~ "spr",
    method == "ccrepe" ~ "ccr",
    method == "sparcc" ~ "spa"
  )) %>%
  tidyr::unite(label, study, method_brief, sep = "_", remove = F)


# keep only relevant columns and make a matrix
global_all_df_mat <- global_all_df_2 %>% 
  dplyr::select(label, nComp1:nnegedge, samples, Chao1, Pielou_J, ave_BC) %>%
  column_to_rownames("label") %>% as.matrix()

# optionally obtain a scatterplot matrix
if(verbose_output) ggpairs(as.data.frame(global_all_df_mat), progress = F)



# look at the number of components (using correlation matrix)
var_to_use <- !(colnames(global_all_df_mat) %in% c("ncomp", "vertConnect1","edgeConnect1","ntaxa"))
psych::fa.parallel(global_all_df_mat[,c(1:5,8:14,17:18)], fa = "pc", n.iter = 100)
Matrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneThe estimated weights for the factor scores are probably incorrect.  Try a different factor score estimation method.
Parallel analysis suggests that the number of factors =  NA  and the number of components =  2 

PCA1 <- psych::principal(global_all_df_mat[,c(1:5,8:14,17:18)], nfactors = 2, rotate = "varimax")
Matrix was not positive definite, smoothing was done
PCA1
Principal Components Analysis
Call: psych::principal(r = global_all_df_mat[, c(1:5, 8:14, 17:18)], 
    nfactors = 2, rotate = "varimax")
Standardized loadings (pattern matrix) based upon correlation matrix

                       RC1  RC2
SS loadings           4.68 4.38
Proportion Var        0.33 0.31
Cumulative Var        0.33 0.65
Proportion Explained  0.52 0.48
Cumulative Proportion 0.52 1.00

Mean item complexity =  1.3
Test of the hypothesis that 2 components are sufficient.

The root mean square of the residuals (RMSR) is  0.15 
 with the empirical chi square  74.92  with prob <  0.17 

Fit based upon off diagonal values = 0.88
biplot(PCA1, xlim.s = c(-1,4), ylim.s = c(-2,5), main = "PCA biplot, all variables")

fullnetscores <- cbind(global_all_df_2,PCA1$scores)
# the score plot
ggplot(fullnetscores, mapping = aes(x = RC1, y = RC2, shape = method, colour = study)) +
  geom_point() +
  labs(title = "all variables") +
  scale_shape_manual(values = c("spieceasi" = 16, "spring" = 17, "ccrepe" = 15, "sparcc" = 18)) +
  scale_color_brewer(type = "qual", palette = "Paired") +
  theme(plot.title = element_text(hjust = 0.5))


var_to_use <- colnames(global_all_df_mat) %in% c("ncomp1", "modularity1","density1","clustCoef1",
                                                 "avPath1", "pep1", "Pielou_J", "ave_BC", "nnodes")
cat("variables to use: ", var_to_use, "\n")
variables to use:  FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE 
psych::fa.parallel(global_all_df_mat[,var_to_use], fa = "pc", n.iter = 100)
Parallel analysis suggests that the number of factors =  NA  and the number of components =  0 

PCA2 <- principal(global_all_df_mat[,var_to_use], nfactors = 2, rotate = "varimax")
PCA2
Principal Components Analysis
Call: principal(r = global_all_df_mat[, var_to_use], nfactors = 2, 
    rotate = "varimax")
Standardized loadings (pattern matrix) based upon correlation matrix

                       RC1  RC2
SS loadings           2.45 2.33
Proportion Var        0.31 0.29
Cumulative Var        0.31 0.60
Proportion Explained  0.51 0.49
Cumulative Proportion 0.51 1.00

Mean item complexity =  1.3
Test of the hypothesis that 2 components are sufficient.

The root mean square of the residuals (RMSR) is  0.22 
 with the empirical chi square  52.69  with prob <  1e-06 

Fit based upon off diagonal values = 0.64
biplot(PCA2)

fullnetscores_2 <- cbind(global_all_df_2,PCA2$scores)
var_acc_RC1 <- round(PCA2$Vaccounted[4,1],3)
var_acc_RC2 <- round(PCA2$Vaccounted[4,2],3)
# the score plot
ggplot(fullnetscores_2, mapping = aes(x = RC1, y = RC2, shape = method, colour = study)) +
  geom_point() +
  labs(title = "selected variables") +
  scale_shape_manual(values = c("spieceasi" = 16, "spring" = 17, "ccrepe" = 15, "sparcc" = 18)) +
  scale_color_brewer(type = "qual", palette = "Paired") +
  labs(x = paste("RC1 (", var_acc_RC1, ")", sep =""),
       x = paste("RC2 (", var_acc_RC2, ")", sep ="")) +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))


ggsave(filename = paste(file.path(output_folder,out_filename_pref), "_PCA.tiff",sep=""), device = "tiff", width = 6, 
       height = 5, units = "in", dpi=600)


if(keep_time) toc()
Comparing global properties: 30.577 sec elapsed

With these many datasets and inference methods the PCA becomes difficult to use. Note how the properties of the network are, as expected, specific to any given inference methods and data-set and how methods based on correlation (sparCC and CCREPE) tend to be closer among them than to methods based on conditional dependence.

The analysis is repeated below on the largest connected component for each network.

if(keep_time) tic("Comparing global properties on LCC")
# create a label and extract matrix for the analysis
global_all_df_3 <- global_lcc_df %>%
  tidyr::separate(dataset, into = c("study", "type", "object"), remove = F) %>%
  select(-type, -object) %>%
  mutate(method_brief = case_when(
    method == "spieceasi" ~ "spi",
    method == "spring" ~ "spr",
    method == "ccrepe" ~ "ccr",
    method == "sparcc" ~ "spa"
  )) %>%
  tidyr::unite(label, study, obj_type, method_brief, sep = "_", remove = F)


# keep only relevant columns and make matrix
global_all_df_mat_2 <- global_all_df_3 %>% 
  dplyr::select(label, lccSize1:nnegedge, samples, Chao1, Pielou_J, ave_BC) %>%
  column_to_rownames("label") %>% as.matrix()
# need to remove an Inf value, I am removing the row

# obtain a scatterplot matrix
if(verbose_output) ggpairs(as.data.frame(global_all_df_mat_2[-3,]), progress = F)



# look at the number of components (using correlation matrix): must exclude
# ave path length and clustering coefficient which contain Inf and NaN

psych::fa.parallel(global_all_df_mat_2[-3,c(1:3,6,9:14,17:18)], fa = "pc", n.iter = 100)
Matrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneMatrix was not positive definite, smoothing was doneThe estimated weights for the factor scores are probably incorrect.  Try a different factor score estimation method.In factor.scores, the correlation matrix is singular, an approximation is used
Matrix was not positive definite, smoothing was done
Parallel analysis suggests that the number of factors =  NA  and the number of components =  1 

PCA1_lcc <- psych::principal(global_all_df_mat_2[-3,c(1:3,6,9:14,17:18)], nfactors = 2, rotate = "varimax")
Matrix was not positive definite, smoothing was doneThe matrix is not positive semi-definite, scores found from Structure loadings
PCA1_lcc
Principal Components Analysis
Call: psych::principal(r = global_all_df_mat_2[-3, c(1:3, 6, 9:14, 
    17:18)], nfactors = 2, rotate = "varimax")
Standardized loadings (pattern matrix) based upon correlation matrix

                       RC1  RC2
SS loadings           5.41 3.23
Proportion Var        0.45 0.27
Cumulative Var        0.45 0.72
Proportion Explained  0.63 0.37
Cumulative Proportion 0.63 1.00

Mean item complexity =  1.3
Test of the hypothesis that 2 components are sufficient.

The root mean square of the residuals (RMSR) is  0.13 
 with the empirical chi square  41.61  with prob <  0.53 

Fit based upon off diagonal values = 0.94
biplot(PCA1_lcc, main = "PCA biplot, all variables")

fullnetscores_lcc <- cbind(global_all_df_3[-3,],PCA1_lcc$scores)
# the score plot
ggplot(fullnetscores_lcc, mapping = aes(x = RC1, y = RC2, shape = method, colour = study)) +
  geom_point() +
  labs(title = "all variables") +
  scale_shape_manual(values = c("spieceasi" = 16, "spring" = 17, "ccrepe" = 15, "sparcc" = 18)) +
  scale_color_brewer(type = "qual", palette = "Paired") +
  theme(plot.title = element_text(hjust = 0.5))


var_to_use_lcc <- colnames(global_all_df_mat) %in% c("lccSize1", "modularity1","density1", "pep1", "Pielou_J", "ave_BC", "nnodes")
cat("variables to use: ", var_to_use_lcc, "\n")
variables to use:  FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE TRUE TRUE 
psych::fa.parallel(global_all_df_mat_2[-3,var_to_use_lcc], fa = "pc", n.iter = 100)
Parallel analysis suggests that the number of factors =  NA  and the number of components =  1 

PCA2_lcc <- principal(global_all_df_mat_2[-3,var_to_use_lcc], nfactors = 2, rotate = "varimax")
PCA2_lcc
Principal Components Analysis
Call: principal(r = global_all_df_mat_2[-3, var_to_use_lcc], nfactors = 2, 
    rotate = "varimax")
Standardized loadings (pattern matrix) based upon correlation matrix

                       RC1  RC2
SS loadings           2.37 1.90
Proportion Var        0.39 0.32
Cumulative Var        0.39 0.71
Proportion Explained  0.55 0.45
Cumulative Proportion 0.55 1.00

Mean item complexity =  1.2
Test of the hypothesis that 2 components are sufficient.

The root mean square of the residuals (RMSR) is  0.12 
 with the empirical chi square  8.19  with prob <  0.085 

Fit based upon off diagonal values = 0.91
biplot(PCA2_lcc)

fullnetscores_2_lcc <- cbind(global_all_df_2[-3,],PCA2_lcc$scores)
# the score plot
var_acc_RC1_lcc <- round(PCA2$Vaccounted[4,1],3)
var_acc_RC2_lcc <- round(PCA2$Vaccounted[4,2],3)
# the score plot
ggplot(fullnetscores_2_lcc, mapping = aes(x = RC1, y = RC2, shape = method, colour = study)) +
  geom_point() +
  labs(title = "selected variables") +
  scale_shape_manual(values = c("spieceasi" = 16, "spring" = 17, "ccrepe" = 15, "sparcc" = 18)) +
  scale_color_brewer(type = "qual", palette = "Paired") +
  labs(x = paste("RC1 (", var_acc_RC1_lcc, ")", sep =""),
       x = paste("RC2 (", var_acc_RC2_lcc, ")", sep ="")) +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))

ggsave(filename = paste(file.path(output_folder,out_filename_pref), "_PCA_lcc.tiff",sep=""), device = "tiff", width = 6, 
       height = 5, units = "in", dpi=600)


if(keep_time) toc()
Comparing global properties on LCC: 34.858 sec elapsed

The results are similar, and there is not much to add. While SPIEC-EASI produces the most sparse networks (and can be a candidate for the detection of the most parsimonious interaction sets), CCREPE and SparCC both may detect indirect interactions and, detecting clusters in this networks may be useful to detect sub-biomes within a given study.

Plotting selected networks.

I will now use tidygraph and ggraph to plot the networks. The thickness of the edges is made proportional to the absolute value of the association measure. Copresence edges are in green, mutual exclusion ones in red. Size of the nodes is made proportional to the total degree (negative degree + positive degree). The color of the nodes is determined by the phylum. Some of this options can be adjusted in the call to the plot_ggraph() function below. Others must be adjusted in the code of the function (all functions are in the source folder).

if(keep_time) tic("Plotting the networks with ggraph")
# create a list of network plots
netplot_list <- vector("list", length = length(tidygraph_list_wstats))
for(i in seq_along(tidygraph_list_wstats)){
  netplot_list_2 <- vector("list", length = length(tidygraph_list_wstats[[i]]))
  dtst <- names(tidygraph_list_wstats)[i]
  for(j in seq_along(tidygraph_list_wstats[[i]])){
    if(!is.tbl_graph(tidygraph_list_wstats[[i]][[j]])){
      netplot_list_2[[j]] <- "no plot to return"
      next
    } else {
      tg <- tidygraph_list_wstats[[i]][[j]]
      mthd <- names(tidygraph_list_wstats[[i]])[j]
      # the default for the argument c0l0r is phylum, otherwise
      # use c0l0r = clust_memb
      # argument lp = "bottom" is the default; "right" is an alternative
      netplot_list_2[[j]] <- plot_ggraph(tidy_graph = tg,
                                         method = mthd,
                                         name = dtst,
                                         lp = "right")
      names(netplot_list_2)[j] <- mthd
      print(netplot_list_2[[j]])
    }
  }
  netplot_list[[i]] <- netplot_list_2
  names(netplot_list)[i] <- dtst
}


rm(tg, dtst, mthd, netplot_list_2)
if(play_audio) beep(sound = 6)
if(keep_time) toc()
Plotting the networks with ggraph: 6.314 sec elapsed

Due to the large number of interactions it is hard to discuss the results. In some of the graphs clusters are clearly evident (and could be highlighted by using the cluster id for colors). The structure of the graphs obtained with the different methods for each given dataset is also different. This may be due to the fact that both the correlation based methods may detect indirect interactions.
In addition, given the scope of this analysis (with emphasis on nodes and edges conserved across different studies) there is little point in discussing individual networks, because this would require going into detail in the experimental approach used in each study.
The following chunk id design to compare network plots build with different methods in a grid. Two of the datasets (chosen because they returned a network for all inference methods and because they had networks of different complexity have been chosen here). However, due to the large number of elements in the graph, plotting all the elements (graph, legend, title) is difficult and may require acting on graphic parameters.

if(keep_time) tic("Plotting and saving networks in a grid")

p1<-netplot_list[[2]][[1]]
p2<-netplot_list[[2]][[2]]
p3<-netplot_list[[2]][[3]]
p4<-netplot_list[[2]][[4]]
ggrid_1 <- egg::ggarrange(p1, p2, p3, p4, nrow = 2, ncol = 2)
fn <- paste(file.path(output_folder,out_filename_pref), "_ST110.tiff",sep="")
ggsave(ggrid_1, filename = fn, dpi = 600, width = 7, height = 7)
ggrid_1

p5<-netplot_list[[9]][[1]]
p6<-netplot_list[[9]][[2]]
p7<-netplot_list[[9]][[3]]
p8<-netplot_list[[9]][[4]]
ggrid_2 <- egg::ggarrange(p5, p6, p7, p8, nrow = 2, ncol = 2)
fn <- paste(file.path(output_folder,out_filename_pref), "_ST136.tiff",sep="")
ggsave(ggrid_2, filename = fn, dpi = 600, width = 7, height = 7)
ggrid_2

if(play_audio) beep(sound = 6)
if(keep_time) toc()
rm(p1, p2, p3, p4, p5, p6, p7, p8)

Node analysis.

When comparing networks from several studies it might be of interest to classify nodes on the basis of their measures of centrality across multiple studies: is there a super hub (i.e. a node which i a hub in several networks)? How are different centrality measures related? Are there nodes which engage more often in negative interactions? Of course it would be pointless to compare across several methods of inference or, given the results above, between ASV and FMBN studies (when aggregation at the genus level is used). This script will produce a few node plotsas a proof of concept. Both SPIEC-EASI and SparCC will be used on data extracted from FMBN and aggregated at the genus level.

if(keep_time) tic("Node analysis")

mymethods <- c("spieceasi","sparcc")
n_to_label <- 20 # max number of nodes to label in the node plots
node_analysis_list <- vector("list", length = length(mymethods))
for(i in seq_along(mymethods)){
  # create the dfs, one for spiec-easi and one for sparCC and only select FMBN
  # only select nodes with degree>0
  FMBN_node_df <- node_stats_df %>%
    dplyr::filter(method == mymethods[i]) %>%
    dplyr::filter(degree>0) %>%
    mutate(Study = as.factor(Study),
           rel_pos_degree = pos_degree / sum(pos_degree)) %>%
    dplyr::rename(tlabel = label)
  
  
  # use a loop to do the node degree graphs, print them and put them in a list
  node_degree_plot_list <- vector("list", length=nlevels(FMBN_node_df$study))
  
  for(j in seq_along(levels(FMBN_node_df$Study))){
    gtitle = paste(levels(FMBN_node_df$Study)[j], mymethods[i], sep = ", ")
    temp_df <- FMBN_node_df %>% 
      dplyr::filter(Study ==levels(FMBN_node_df$Study)[j]) %>%
      mutate(dgr = pos_degree + neg_degree) %>%
      arrange(-dgr) %>%
      rowid_to_column() %>% 
      mutate(to_label = if_else((rowid<=20 | is_hub), tlabel, NA_character_))
    
    ave_degree = mean(temp_df$dgr, na.rm = T)
    ave_pos_degree = mean(temp_df$pos_degree, na.rm = T)
    # creating the plot, only the names of the top 20 nodes (in terms of degree)
    # are plotted, hubs are always plotted
    ggp <- ggplot(
      temp_df,
      mapping = aes(
        x = pos_degree,
        y = dgr,
        label = str_trunc(genus, 12, side = "center")
      )
    ) +
      geom_smooth(method = "lm", linetype = 3, se = F, color = I("black"), show.legend = F) +
      geom_point(mapping = aes(color = phylum,
                               size = relAbundance,
                               alpha = between)) +
      geom_text_repel(show.legend = F, max.overlaps = 20, alpha = I(0.5)) +
      geom_abline(slope = 1, intercept = 0, linetype = 1) +
      geom_hline(yintercept = ave_degree, linetype = 3, show.legend = F) +
      geom_vline(xintercept = ave_pos_degree, linetype = 3, show.legend = F) +
      scale_alpha_continuous(range = c(0.4, 1)) +
      scale_size_continuous(range = c(1,6)) +
      labs(
        x = "positive degree",
        y = "degree",
        size = "relative abundance",
        alpha = "betweenness",
        title = gtitle
      ) +
      theme_bw() +
      theme(plot.title = element_text(hjust = 0.5))
    
    print(ggp)
    node_degree_plot_list[[j]] <- ggp
    names(node_degree_plot_list[[j]]) <- gtitle
  }
  file_name <- paste(file.path(output_folder,out_filename_pref), "_", mymethods[i], "_nodeplots.Rdata",sep="")
  save(node_degree_plot_list, file = file_name)
  
  # calculate frequency for hubs (meaningful only if you have many taxa and the percentile for hub detection is low, say 0.75-0.90)
  n_datasets <- dplyr::n_distinct(FMBN_node_df$dataset)
  FMBN_node_df_hubs <- FMBN_node_df %>%
    dplyr::filter(is_hub == T) %>%
    group_by(tlabel, phylum, class) %>%
    summarise(hub_freq = n()/n_datasets) %>%
    arrange(-hub_freq)
  cat("method", mymethods[i], "hub frequency", "\n")
  head(FMBN_node_df_hubs, 20)
  # save elements in the list
node_analysis_list[[i]] <- list(
  node_df = FMBN_node_df,
  plot_list = node_degree_plot_list,
  node_df_hubs = FMBN_node_df_hubs
)
names(node_analysis_list)[i]<- mymethods[i]
  
}
method spieceasi hub frequency 
method sparcc hub frequency 

if(play_audio) beep(sound = 6)
if(keep_time) toc()
Node analysis: 7.264 sec elapsed
rm(FMBN_node_df, node_degree_plot_list, temp_df, FMBN_node_df_hubs)

The node plots provide a number of visual cues:

  • the horizontal and vertical dotted lines show the average value for degree and positive degree and may help in identifying nodes whose values are far from the mean;

  • the diagonal continuous line corresponds to points for which positive degree and degree are equal; points above the line have at least 1 negative interaction

  • the diagonal dotted line is the linear regression line for degree as a function of positive degree; its position and slope relative to the previous line indicates on average how much the ratio between total degree and positive degree changes as a function of degree; data points which are far from this line also have an unusual ratio between the two values compared to the other nodes in the same dataset.

  • negative hubs will be located close to the upper left corner; positive hubs will be closer to the right of the plot.

Even with SPIEC-EASI the number of plotted nodes is high for some studies (but very low for others), and one my consider plotting a given number of nodes (the ones with top degree? top eigenvector centrality?, some combination of both) to simplify the plot.
Said that, although beetweenness centrality may be interesting to identify “bottleneck” taxa, looking at betweenness is difficult (the shades of alpha = transparency are visually difficult to separate), but otherwise degree, positive degree and abundance are all easy to visualise.

Edge analysis.

Edge analysis only makes sense once one has settled for meaningful questions and selected a large and diverse enough set of studies. Questions which might be relevant:

  • how stable is an edge?

    • within a study across methods

    • across studies with a given method

  • given an inference method, can one assemble a consensus network using as weights the number of times a given edge occurs? The frequency of occurrence?

  • how important is an edge (as measured by edge betweenness)? One must choose a meaningful context for this

  • are co-occurrence relationships more frequent among members of the same class or family (and the reverse for mutual exclusion)? This only makes sense within a given method and must be corrected by the frequency of ech given larger taxon.

  • given a (possibly important) microorganism, with which microorganisms is it most frequently associated with positive or negative associations? This only makes sense within a given method and once one has analyzed a large number of studies

  • for moderately large networks, which is the cumulative distribution of edge betweenness? Does this hold any importance in terms of structure of the networks?

if(keep_time) tic("Edge analysis")
# this calculates the empirical quantile of edge betweenness
# by dataset and method: I suppose one can then select those >0.95
# to get a sort of hubness for edges, but it makes sense only for 
# large number of edges
edge_list_df <- edge_list_df %>% group_by(dataset, method) %>%
  mutate(ebq = ecdf(edge_betw)(edge_betw)) %>% ungroup()

# calculate the frequency of edges, by study and type and 
# median value and IQR for IQR
# this only checks for conserved edges across methods
edge_freq_bystudy <- edge_list_df %>% 
  group_by(dataset, asso_type, edge_name) %>% 
  summarise(n = n(),
            med_ebq = median(ebq),
            iqr_ebq = IQR(ebq)) %>%
  arrange(-n, -med_ebq, ) %>% 
  ungroup()
`summarise()` has grouped output by 'dataset', 'asso_type'. You can override using the `.groups` argument.
# I am now generating a plot may be with the top 50 edges, only for FMBN studies
edge_freq_bystudy_FMBN <- edge_freq_bystudy %>%
  dplyr::filter(str_detect(dataset, "FMBN")) %>%
  arrange(-n, -med_ebq)

# let's try a plot (gives an emphasis to most stable edges)
slice_to_plot <- slice(edge_freq_bystudy_FMBN, 1:50) %>%
    mutate(edge_name = forcats::fct_reorder(edge_name, med_ebq)) 

ggplot(slice_to_plot, 
       mapping = aes(x = edge_name, y = med_ebq, size = n, color = asso_type)) +
  geom_point() + 
  coord_flip() +
  labs(x = "edge", y = "edge betweenness quantile",
      size = "edge freq.") +
    scale_colour_manual(values = c("green","red"))

Many of the most stable edges include gut bacteria and probably represent cluster of interactions from this environment which are carried over to milk and then, possibly, cheese (some studies analyzed here include also environmental samples, like feces).
The analysis here was carried out by groping by dataset and association type, therefore the frequency is by dataset and a frequency of 4 indicates that a given edge was detected by all 4 methods in a single dataset. The code can be easily modified to detect edges which are conserved across different studies and different methods. I will now try to estimate the taxonomic assortativity. To do this, the odds ratio (OR) for copresence links (given that the nodes belong to the same family) and mutual exlusion links (given that the nodes belong to different families) is calculated and the significance is evaluated using epi.2by2() function.

# same, by method and assotype, only for FMBN
# only meaningful for high number of studies
edge_freq_bymethod <- edge_list_df %>% 
  dplyr::filter(str_detect(dataset, "FMBN")) %>%
  group_by(method, asso_type) %>% 
  count(edge_name) %>%
  arrange(method, asso_type, -n)

# The following section evaluates taxonomic assortativity (i.e. evaluates if 
# copresence associations are more frequent among members of the same 
# family, order or class). The odds ratio of copresence relationships within
# the same family is calculated using epiR::epi.2by2()

# first, add taxonomy to the 
unique_tax <- node_stats_df %>% 
  dplyr::select(label, domain:species) %>%
  distinct()

edge_list_df_wtaxa <- edge_list_df
edge_list_df_wtaxa <- left_join(edge_list_df_wtaxa, 
                                dplyr::select(unique_tax, label, class, order, family), 
                                by = c("from_name" = "label")) %>%
  dplyr::rename(from_class = class, from_order = order, from_family = family)
edge_list_df_wtaxa <- left_join(edge_list_df_wtaxa, 
                                dplyr::select(unique_tax, label, class, order, family), 
                                by = c("to_name" = "label")) %>%
  dplyr::rename(to_class = class, to_order = order, to_family = family)
# check if same family or same class
edge_list_df_wtaxa <- edge_list_df_wtaxa %>%
  mutate(same_class = if_else(from_class == to_class, T, F),
         same_order = if_else(from_order == to_order, T, F),
         same_family = if_else(from_family == to_family, T, F)
         )

# use a loop to calculate odds ratios and probabilities
# get the number of studies

edge_list_df_wtaxa <- edge_list_df_wtaxa %>% 
  separate(dataset, into = c("study", "col2", "col3"), remove = F) %>%
  select(-col2, -col3) %>%
  mutate(study = as.factor(study),
         sf = factor(same_family, levels = c("TRUE", "FALSE")),
         so = factor(same_order, levels = c("TRUE", "FALSE")),
         sc = factor(same_class, levels = c("TRUE", "FALSE"))
  )

assort_results_list <- vector(mode = "list", length = nlevels(edge_list_df_wtaxa$study))
taxo_level_selection <- "family"
for(i in seq_along(levels(edge_list_df_wtaxa$study))){
  input_df_study <- edge_list_df_wtaxa %>% dplyr::filter(study == levels(study)[i]) %>%
    mutate(method = as.factor(method))
  inner_result_list <- vector(mode = "list", length = nlevels(input_df_study$method))
  for(j in seq_along(levels(input_df_study$method))){
    input_df_method <- input_df_study %>% 
      mutate(method = as.factor(method)) %>%
      dplyr::filter(method == levels(method)[j])
    inner_result_list[[j]]<-try(odds_ratio(input_df_method, taxo_level = taxo_level_selection))
    names(inner_result_list)[j]<- levels(input_df_method$method)[j]
  }
  assort_results_list[[i]] <- inner_result_list
  names(assort_results_list)[i] <- levels(edge_list_df_wtaxa$study)[i]
}
NaNs producedNaNs producedError in if (any(x2)) upp[x2] <- rep(1, sum(x2)) : 
  missing value where TRUE/FALSE needed
NaNs producedError in uniroot(function(or) { : 
  f() values at end points not of opposite sign
NaNs producedNaNs producedError in if (sum(A) > 0 & sum(B) > 0 & sum(C) > 0 & sum(D) > 0) { : 
  missing value where TRUE/FALSE needed
Error in if (any(x2)) upp[x2] <- rep(1, sum(x2)) : 
  missing value where TRUE/FALSE needed
NaNs producedNaNs producedNaNs producedNaNs producedError in while (chi2 <= z) { : missing value where TRUE/FALSE needed
Error in if (any(x2)) upp[x2] <- rep(1, sum(x2)) : 
  missing value where TRUE/FALSE needed
Error in if (any(x2)) upp[x2] <- rep(1, sum(x2)) : 
  missing value where TRUE/FALSE needed
NaNs producedNaNs producedNaNs producedNaNs producedNaNs producedError in uniroot(function(or) { : 
  f() values at end points not of opposite sign
NaNs producedNaNs produced
# clean-up the list and put together the data frames

df_list <- vector(mode = "list", length = length(assort_results_list))
for(i in seq_along(assort_results_list)){
  df_list[[i]] <- df_return(assort_results_list[[i]]) 
  names(df_list)[i]<-names(assort_results_list)[i]
}

assort_results_df <- bind_rows(df_list, .id = "study")

# create dummy ORest values by replacing Inf
assort_results_df <- assort_results_df %>%
  mutate(OR_est_wdummy = if_else(OR_est==Inf, 99, OR_est)) %>%
  mutate(lOR_est_wdummy = log(OR_est_wdummy),
         significant = if_else(OR_p.value <=0.05, T, F)) 

# plot, studies are pooled
ggplot(assort_results_df, mapping = aes(x = assort_test, y = lOR_est_wdummy, colour = significant)) +
  facet_wrap(~method) +
  geom_jitter(width = 0.2) +
  labs(y = "odds ratio")


rm(assort_results_list, input_df_study, inner_result_list, input_df_method, i, j, df_list)

if(play_audio) beep(sound = 6)
if(keep_time) toc()
Edge analysis: 3.847 sec elapsed

Even if the odds ratios are very high in some cases (actually Inf, due to division by 0) they are not significant. My conclusion is that there is not sufficient support for the occurrence of taxonomic assortativity (i.e. for a significantly higher probability of having a copresence link between members of the same family).
Similar results can be obtained when looking at the order and class level.

Citations and copyright.

Citations.

Citations for the metataxonomic studies used in this analysis are in the study_metadata data frame (if any). The main package used in this analysis is NetCoMi, and of course, Phyloseq. Citations for all packages are below.


all_packages <- c("base", .cran_packages, .bioc_packages, .github_packages)
map(all_packages, citation)
no date field in DESCRIPTION file of package ‘SpiecEasi’no date field in DESCRIPTION file of package ‘NetCoMi’
[[1]]

To cite R in publications use:

  R Core Team (2021). R: A language and environment for statistical computing. R Foundation for
  Statistical Computing, Vienna, Austria. URL https://www.R-project.org/.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {R: A Language and Environment for Statistical Computing},
    author = {{R Core Team}},
    organization = {R Foundation for Statistical Computing},
    address = {Vienna, Austria},
    year = {2021},
    url = {https://www.R-project.org/},
  }

We have invested a lot of time and effort in creating R, please cite it when using it for data analysis.
See also ‘citation("pkgname")’ for citing R packages.


[[2]]

To cite package ‘devtools’ in publications use:

  Hadley Wickham, Jim Hester and Winston Chang (2021). devtools: Tools to Make Developing R Packages
  Easier. R package version 2.4.0. https://CRAN.R-project.org/package=devtools

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {devtools: Tools to Make Developing R Packages Easier},
    author = {Hadley Wickham and Jim Hester and Winston Chang},
    year = {2021},
    note = {R package version 2.4.0},
    url = {https://CRAN.R-project.org/package=devtools},
  }


[[3]]

The ‘parallel’ package is part of R.  To cite R in publications use:

  R Core Team (2021). R: A language and environment for statistical computing. R Foundation for
  Statistical Computing, Vienna, Austria. URL https://www.R-project.org/.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {R: A Language and Environment for Statistical Computing},
    author = {{R Core Team}},
    organization = {R Foundation for Statistical Computing},
    address = {Vienna, Austria},
    year = {2021},
    url = {https://www.R-project.org/},
  }

We have invested a lot of time and effort in creating R, please cite it when using it for data analysis.
See also ‘citation("pkgname")’ for citing R packages.


[[4]]

  Wickham et al., (2019). Welcome to the tidyverse. Journal of Open Source Software, 4(43), 1686,
  https://doi.org/10.21105/joss.01686

A BibTeX entry for LaTeX users is

  @Article{,
    title = {Welcome to the {tidyverse}},
    author = {Hadley Wickham and Mara Averick and Jennifer Bryan and Winston Chang and Lucy D'Agostino McGowan and Romain François and Garrett Grolemund and Alex Hayes and Lionel Henry and Jim Hester and Max Kuhn and Thomas Lin Pedersen and Evan Miller and Stephan Milton Bache and Kirill Müller and Jeroen Ooms and David Robinson and Dana Paige Seidel and Vitalie Spinu and Kohske Takahashi and Davis Vaughan and Claus Wilke and Kara Woo and Hiroaki Yutani},
    year = {2019},
    journal = {Journal of Open Source Software},
    volume = {4},
    number = {43},
    pages = {1686},
    doi = {10.21105/joss.01686},
  }


[[5]]

To cite 'igraph' in publications use:

  Csardi G, Nepusz T: The igraph software package for complex network research, InterJournal, Complex
  Systems 1695. 2006. https://igraph.org

A BibTeX entry for LaTeX users is

  @Article{,
    title = {The igraph software package for complex network research},
    author = {Gabor Csardi and Tamas Nepusz},
    journal = {InterJournal},
    volume = {Complex Systems},
    pages = {1695},
    year = {2006},
    url = {https://igraph.org},
  }


[[6]]

To cite package ‘VennDiagram’ in publications use:

  Hanbo Chen (2018). VennDiagram: Generate High-Resolution Venn and Euler Plots. R package version 1.6.20.
  https://CRAN.R-project.org/package=VennDiagram

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {VennDiagram: Generate High-Resolution Venn and Euler Plots},
    author = {Hanbo Chen},
    year = {2018},
    note = {R package version 1.6.20},
    url = {https://CRAN.R-project.org/package=VennDiagram},
  }

ATTENTION: This citation information has been auto-generated from the package DESCRIPTION file and may
need manual editing, see ‘help("citation")’.


[[7]]

To cite package ‘tidygraph’ in publications use:

  Thomas Lin Pedersen (2020). tidygraph: A Tidy API for Graph Manipulation. R package version 1.2.0.
  https://CRAN.R-project.org/package=tidygraph

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {tidygraph: A Tidy API for Graph Manipulation},
    author = {Thomas Lin Pedersen},
    year = {2020},
    note = {R package version 1.2.0},
    url = {https://CRAN.R-project.org/package=tidygraph},
  }


[[8]]

To cite package ‘lobstr’ in publications use:

  Hadley Wickham (2019). lobstr: Visualize R Data Structures with Trees. R package version 1.1.1.
  https://CRAN.R-project.org/package=lobstr

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {lobstr: Visualize R Data Structures with Trees},
    author = {Hadley Wickham},
    year = {2019},
    note = {R package version 1.1.1},
    url = {https://CRAN.R-project.org/package=lobstr},
  }


[[9]]

To cite package ‘tictoc’ in publications use:

  Sergei Izrailev (2021). tictoc: Functions for Timing R Scripts, as Well as Implementations of Stack and
  List Structures. R package version 1.0.1. https://CRAN.R-project.org/package=tictoc

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {tictoc: Functions for Timing R Scripts, as Well as Implementations of
Stack and List Structures},
    author = {Sergei Izrailev},
    year = {2021},
    note = {R package version 1.0.1},
    url = {https://CRAN.R-project.org/package=tictoc},
  }

ATTENTION: This citation information has been auto-generated from the package DESCRIPTION file and may
need manual editing, see ‘help("citation")’.


[[10]]

To cite package ‘beepr’ in publications use:

  Rasmus Bååth (2018). beepr: Easily Play Notification Sounds on any Platform. R package version 1.3.
  https://CRAN.R-project.org/package=beepr

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {beepr: Easily Play Notification Sounds on any Platform},
    author = {Rasmus Bååth},
    year = {2018},
    note = {R package version 1.3},
    url = {https://CRAN.R-project.org/package=beepr},
  }


[[11]]

To cite the psych package in publications use:

  Revelle, W. (2020) psych: Procedures for Personality and Psychological Research, Northwestern
  University, Evanston, Illinois, USA, https://CRAN.R-project.org/package=psych Version = 2.1.3,.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {psych: Procedures for Psychological, Psychometric, and Personality Research},
    author = {William Revelle},
    organization = { Northwestern University},
    address = { Evanston, Illinois},
    year = {2021},
    note = {R package version 2.1.3},
    url = {https://CRAN.R-project.org/package=psych},
  }


[[12]]

To cite package ‘GGally’ in publications use:

  Barret Schloerke, Di Cook, Joseph Larmarange, Francois Briatte, Moritz Marbach, Edwin Thoen, Amos Elberg
  and Jason Crowley (2021). GGally: Extension to 'ggplot2'. R package version 2.1.1.
  https://CRAN.R-project.org/package=GGally

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {GGally: Extension to 'ggplot2'},
    author = {Barret Schloerke and Di Cook and Joseph Larmarange and Francois Briatte and Moritz Marbach and Edwin Thoen and Amos Elberg and Jason Crowley},
    year = {2021},
    note = {R package version 2.1.1},
    url = {https://CRAN.R-project.org/package=GGally},
  }


[[13]]

To cite package ‘ggrepel’ in publications use:

  Kamil Slowikowski (2021). ggrepel: Automatically Position Non-Overlapping Text Labels with 'ggplot2'. R
  package version 0.9.1. https://CRAN.R-project.org/package=ggrepel

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {ggrepel: Automatically Position Non-Overlapping Text Labels with
'ggplot2'},
    author = {Kamil Slowikowski},
    year = {2021},
    note = {R package version 0.9.1},
    url = {https://CRAN.R-project.org/package=ggrepel},
  }


[[14]]

To cite package ‘ggraph’ in publications use:

  Thomas Lin Pedersen (2021). ggraph: An Implementation of Grammar of Graphics for Graphs and Networks. R
  package version 2.0.5. https://CRAN.R-project.org/package=ggraph

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {ggraph: An Implementation of Grammar of Graphics for Graphs and Networks},
    author = {Thomas Lin Pedersen},
    year = {2021},
    note = {R package version 2.0.5},
    url = {https://CRAN.R-project.org/package=ggraph},
  }


[[15]]

To cite package ‘egg’ in publications use:

  Baptiste Auguie (2019). egg: Extensions for 'ggplot2': Custom Geom, Custom Themes, Plot Alignment,
  Labelled Panels, Symmetric Scales, and Fixed Panel Size. R package version 0.4.5.
  https://CRAN.R-project.org/package=egg

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {egg: Extensions for 'ggplot2': Custom Geom, Custom Themes, Plot
Alignment, Labelled Panels, Symmetric Scales, and Fixed Panel
Size},
    author = {Baptiste Auguie},
    year = {2019},
    note = {R package version 0.4.5},
    url = {https://CRAN.R-project.org/package=egg},
  }


[[16]]

To cite package ‘epiR’ in publications use:

  Mark Stevenson, Evan Sergeant with contributions from Telmo Nunes, Cord Heuer, Jonathon Marshall, Javier
  Sanchez, Ron Thornton, Jeno Reiczigel, Jim Robison-Cox, Paola Sebastiani, Peter Solymos, Kazuki Yoshida,
  Geoff Jones, Sarah Pirikahu, Simon Firestone, Ryan Kyle, Johann Popp, Mathew Jay and Charles Reynard.
  (2021). epiR: Tools for the Analysis of Epidemiological Data. R package version 2.0.19.
  https://CRAN.R-project.org/package=epiR

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {epiR: Tools for the Analysis of Epidemiological Data},
    author = {Mark Stevenson and Evan Sergeant with contributions from Telmo Nunes and Cord Heuer and Jonathon Marshall and Javier Sanchez and Ron Thornton and Jeno Reiczigel and Jim Robison-Cox and Paola Sebastiani and Peter Solymos and Kazuki Yoshida and Geoff Jones and Sarah Pirikahu and Simon Firestone and Ryan Kyle and Johann Popp and Mathew Jay and Charles Reynard.},
    year = {2021},
    note = {R package version 2.0.19},
    url = {https://CRAN.R-project.org/package=epiR},
  }

ATTENTION: This citation information has been auto-generated from the package DESCRIPTION file and may
need manual editing, see ‘help("citation")’.


[[17]]

To cite package ‘BiocManager’ in publications use:

  Martin Morgan (2021). BiocManager: Access the Bioconductor Project Package Repository. R package version
  1.30.12. https://CRAN.R-project.org/package=BiocManager

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {BiocManager: Access the Bioconductor Project Package Repository},
    author = {Martin Morgan},
    year = {2021},
    note = {R package version 1.30.12},
    url = {https://CRAN.R-project.org/package=BiocManager},
  }


[[18]]

To cite package ‘genefilter’ in publications use:

  R. Gentleman, V. Carey, W. Huber and F. Hahne (2021). genefilter: genefilter: methods for filtering
  genes from high-throughput experiments. R package version 1.72.1.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {genefilter: genefilter: methods for filtering genes from high-throughput
experiments},
    author = {R. Gentleman and V. Carey and W. Huber and F. Hahne},
    year = {2021},
    note = {R package version 1.72.1},
  }

ATTENTION: This citation information has been auto-generated from the package DESCRIPTION file and may
need manual editing, see ‘help("citation")’.


[[19]]

To cite phyloseq in publications, or otherwise credit, please use:

  phyloseq: An R package for reproducible interactive analysis and graphics of microbiome census data.
  Paul J. McMurdie and Susan Holmes (2013) PLoS ONE 8(4):e61217.

A BibTeX entry for LaTeX users is

  @Article{,
    author = {Paul J. McMurdie and Susan Holmes},
    journal = {PLoS ONE},
    pages = {e61217},
    title = {phyloseq: An R package for reproducible interactive analysis and graphics of microbiome census data},
    volume = {8},
    number = {4},
    year = {2013},
    url = {http://dx.plos.org/10.1371/journal.pone.0061217},
  }


[[20]]

To cite package ‘SpiecEasi’ in publications use:

  Zachary Kurtz, Christian Mueller, Emily Miraldi and Richard Bonneau (2020). SpiecEasi: Sparse Inverse
  Covariance for Ecological Statistical Inference. R package version 1.0.7.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {SpiecEasi: Sparse Inverse Covariance for Ecological Statistical Inference},
    author = {Zachary Kurtz and Christian Mueller and Emily Miraldi and Richard Bonneau},
    year = {2020},
    note = {R package version 1.0.7},
  }


[[21]]

To cite package ‘NetCoMi’ in publications use:

  Stefanie Peschel (2021). NetCoMi: Network Construction and Comparison for Microbiome Data. R package
  version 1.0.2.

A BibTeX entry for LaTeX users is

  @Manual{,
    title = {NetCoMi: Network Construction and Comparison for Microbiome Data},
    author = {Stefanie Peschel},
    year = {2021},
    note = {R package version 1.0.2},
  }

ATTENTION: This citation information has been auto-generated from the package DESCRIPTION file and may
need manual editing, see ‘help("citation")’.
# for reproducibility you should also run
# sessionInfo()
LS0tCnRpdGxlOiAiSW5mZXJlbmNlIGFuZCBjb21wYXJpc29uIG9mIG1pY3JvYmlhbCBhc3NvY2lhdGlvbiBuZXR3b3JrcyIKc3VidGl0bGU6ICJJbmZlcnJpbmcgbmV0d29ya3MgZnJvbSBGTUJOIGRhdGEsIGdlbnVzIGxldmVsLCBwYXJ0IDMgKGFuZCBleGFtcGxlIHdvcmtmbG93IgphdXRob3I6ICJFLiBQYXJlbnRlLCBTQUZFIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiAlWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQoKIyBJbnN0YWxsL2xvYWQgcGFja2FnZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgouYmlvY19wYWNrYWdlcyA8LSBjKCJCaW9jTWFuYWdlciIsICJnZW5lZmlsdGVyIiwgInBoeWxvc2VxIikKLmNyYW5fcGFja2FnZXMgPC0gYygiZGV2dG9vbHMiLCAicGFyYWxsZWwiLCAidGlkeXZlcnNlIiwgImlncmFwaCIsIAogICAgICAgICAgICAgICAgICAgICJWZW5uRGlhZ3JhbSIsICJ0aWR5Z3JhcGgiLCAibG9ic3RyIiwgInRpY3RvYyIsICJiZWVwciIsCiAgICAgICAgICAgICAgICAgICAgInBzeWNoIiwgIkdHYWxseSIsICJnZ3JlcGVsIiwgImdncmFwaCIsICJlZ2ciLCAiZXBpUiIpCi5naXRodWJfcGFja2FnZXMgPC0gYygiU3BpZWNFYXNpIiwiTmV0Q29NaSIpCiMgbG9ic3RyIGlzIGNhbGxlZCBmb3IgbG9ic3RyOjpvYmpfc2l6ZSgpCiMgZWdnIGlzIG9ubHkgdXNlZCB0byBhcnJhbmdlIHBsb3RzIGluIGdyaWRzCgouaW5zdCA8LSAuYmlvY19wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCmlmKGFueSghLmluc3QpKSB7CiAgaWYoIS5pbnN0WzFdKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCiAgICAuaW5zdCA8LSAuYmlvY19wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCiAgICB9CiAgaWYoYW55KCEuaW5zdFsyOmxlbmd0aCguaW5zdCldKSkgewogICAgQmlvY01hbmFnZXI6Omluc3RhbGwoLmJpb2NfcGFja2FnZXNbIS5pbnN0XSwgYXNrID0gRikKICAgIH0KfQoKLmluc3QgPC0gLmNyYW5fcGFja2FnZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKQppZihhbnkoIS5pbnN0KSkgewogIGluc3RhbGwucGFja2FnZXMoLmNyYW5fcGFja2FnZXNbIS5pbnN0XSkKfQoKLmluc3QgPC0gLmdpdGh1Yl9wYWNrYWdlcyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpCgojIGNhcmVmdWwgbm93LCBiZWNhdXNlIHRoZSBpbnN0YWxsYXRpb24gb2YgTmV0Q29NaSBjYW4gYmUgYnVnZ3kKIyBpdCB1c3VhbGx5IGZhaWxzIGJlY2F1c2UgaWYgZmFpbHMgdG8gaW5zdGFsbCBvbmUgb2YgdGhlIG1pc3NpbmcgZGVwZW5kZW5jaWVzLAojIGlmIGFueS4gSWYgdGhpcyBoYXBwZW5zLCBpbnN0YWxsIHRoZSBkZXBlbmRlbmNpZXMgZmlyc3QuLi4KCmlmKGFueSghLmluc3QpKXsKICByZXF1aXJlKGRldnRvb2xzKQogIGlmKCEuaW5zdFsxXSkgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJ6ZGsxMjMvU3BpZWNFYXNpIikKICBpZighLmluc3RbMl0pIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic3RlZnBlc2NoZWwvTmV0Q29NaSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcGVuZGVuY2llcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwb3MgPSBjKCJodHRwczovL2Nsb3VkLnItcHJvamVjdC5vcmcvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQmlvY01hbmFnZXI6OnJlcG9zaXRvcmllcygpKSkKfQoKIyBMb2FkIHBhY2thZ2VzIGludG8gc2Vzc2lvbiwgYW5kIHByaW50IHBhY2thZ2UgdmVyc2lvbgpzYXBwbHkoYyguY3Jhbl9wYWNrYWdlcywgLmJpb2NfcGFja2FnZXMsIC5naXRodWJfcGFja2FnZXMpLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCgojIG90aGVyIHNldHVwIG9wZXJhdGlvbnMKb3BhciA8LSBwYXIobm8ucmVhZG9ubHk9VFJVRSkgCnBhcihhc2s9RikgCiMgc2V0LnNlZWQoMTIzNCkgCiMgcGxheSBhdWRpbyBub3RpZmljYXRpb25zCnBsYXlfYXVkaW8gPC0gVAojIHRpbWUgaW1wb3J0YW50IHN0ZXBzCmtlZXBfdGltZSA8LSBUCiMgdmVyYm9zZSBvdXRwdXQ6IHdpbGwgcHJpbnQgYWRkaXRpb25hbCBvYmplY3RzIGFuZCBtZXNzYWdlcwp2ZXJib3NlX291dHB1dCA8LSBUCgojIGxvYWQgdGhlIGZ1bmN0aW9ucyBhbmQgcmV0dXJuIGEgcmVwb3J0CnNvdXJjZShmaWxlLnBhdGgoInNvdXJjZSIsIk1BTl9mdW5jdGlvbnMuUiIpKQppZihwbGF5X2F1ZGlvKSBiZWVwKHNvdW5kID0gNikgIyBub3RpZmljYXRpb24KYGBgCgojIEludHJvZHVjdGlvbi4gIAoKVGhpcyBub3RlYm9vayByZXBvcnRzIG9uIHRoZSBpbmZlcmVuY2Ugb2YgbWljcm9iaWFsIGFzc29jaWF0aW9uIG5ldHdvcmtzIGZyb20gbWV0YS10YXhvbm9taWMgZGF0YSBleHRyYWN0ZWQgZnJvbSBbRGFpcnlGTUJOXShodHRwOi8vZHguZG9pLm9yZy8xMC4xNzYzMi8zY3dmNzI5cDM0LjMpIHVzaW5nIHBhY2thZ2UgW05ldENvTWldKGh0dHBzOi8vZ2l0aHViLmNvbS9zdGVmcGVzY2hlbC9OZXRDb01pKSwgYWZ0ZXIgc29tZSBwcmUtcHJvY2Vzc2luZyBjYXJyaWVkIG91dCBtb3N0bHkgd2l0aCBwYWNrYWdlIFtwaHlsb3NlcV0oaHR0cHM6Ly9qb2V5NzExLmdpdGh1Yi5pby9waHlsb3NlcS8pLiAgCgpUaGUgb2JqZWN0aXZlIG9mIHRoaXMgYW5hbHlzaXMgaXM6ICAKCiogaW5mZXIgbWljcm9iaWFsIGFzc29jaWF0aW9uIG5ldHdvcmtzIChhdCB0aGUgZ2VudXMgbGV2ZWwpIGZvciBzZWxlY3RlZCBjaGVlc2Ugc3R1ZGllcyBzdG9yZWQgaW4gRGFpcnlGTUJOIHVzaWduIGRpZmZlcmVudCBpbmZlcmVuY2UgbWV0aG9kcyAgCgoqIGlkZW50aWZ5IGFzc29jaWF0aW9ucyB3aGljaCBhcmUgY29uc2VydmVkIGFjcm9zcyBtZXRob2RzICAKCiogY29tcGFyZSBkaWZmZXJlbnQgaW5mZXJlbmNlIG1ldGhvZHMgCgoqIGlkZW50aWZ5IGFuZCBjb21wYXJlIGh1YnMgaW4gZGlmZmVyZW50IG5ldHdvcmtzCiAgClRoZSBkYXRhIHVzZWQgaW4gdGhpcyBzY3JpcHQgaW5jbHVkZSBzdHVkaWVzIGZyb20gRk1CTiB2ZXJzaW9ucyAzLjIsIHdpdGggZGlmZmVyZW50IHBsYXRmb3JtcyBhbmQgdGFyZ2V0LCBidXQgYW5hbHl6ZWQgdXNpbmcgdGhlIHNhbWUgcGlwZWxpbmUgYmFzZWQgb24gREFEQTIuIFRoZSBzY3JpcHQgaXMgZGVzaWduZWQgdG8gd29yayB3aXRoIHBoeWxvc2VxIG9iamVjdHMgZXh0cmFjdGVkIGZyb20gRm9vZE1pY3JvYmlvbmV0IG9yIERhaXJ5Rk1CTiB1c2luZyB0aGUgU2hpbnlGTUJOIGFwcCBEYWlyeUZNQk4gY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBbTWVuZGVsZXkgZGF0YV0oaHR0cHM6Ly9kYXRhLm1lbmRlbGV5LmNvbS9kYXRhc2V0cy8zY3dmNzI5cDM0LzMpOiBuZXcgdmVyc2lvbnMgYXJlIGR1ZSBzb29uLiAgClRoZSBzY3JpcHQgd2lsbCBhbHNvIHdvcmsgY29ycmVjdGx5IHdpdGggcGh5bG9zZXEgb2JqZWN0cyBnZW5lcmF0ZWQgdXNpbmcgdGhlIERBREEyIFtCaW9Db25kdWN0b3IgcGlwZWxpbmVdKGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL3R1dG9yaWFsLmh0bWwpIGJ1dCB0aGUgdXNlIG9mIFNJTFZBIGFzIGEgdGF4b25vbWljIGRhdGFiYXNlIGlzIGFkdmlzYWJsZS4gIFdpdGggc29tZSBhZGFwdGF0aW9ucyB0aGUgc2NyaXB0IG1heSB3b3JrIHdpdGggcGh5bG9zZXEgb2JqZWN0cyBnZW5lcmF0ZWQgdXNpbmcgb3RoZXIgcGlwZWxpbmVzLiAgCgojIEFuYWx5c2lzIHdvcmtmbG93LiAgCgoxLiAqKnN0YXJ0IGZyb20gbiAobj4xKSBgcGh5bG9zZXFgIG9iamVjdHMqKi4gSW4gdGhpcyBhbmFseXNpcyBJIHdpbGwgdXNlIHBoeWxvc2VxIG9iamVjdHMgZXh0cmFjdGVkIGZyb20gRGFpcnlGTUJOICh3aGljaCwgaW4gdHVybiwgaW5jbHVkZXMgYWxsIHN0dWRpZXMgb24gZGFpcnkgcHJvZHVjdHMgc3RvcmVkIGluIEZvb2RNaWNyb2Jpb25ldCkuIEFsdGhvdWdoIHBvb2xpbmcgYXQgdGhlIGdlbnVzIGxldmVsIGlzIGxpa2VseSB0byBtdWRkbGUgc29tZSBpbnRlcmFjdGlvbnMsIGl0IGl0IHRoZSBvbmx5IHJlYXNvbmFibGUgY2hvaWNlIHdoZW4gY29tcGFyaW5nIHN0dWRpZXMgd2l0aCB2YXN0bHkgZGlmZmVyZW50IHRhcmdldHMsIHNlcXVlbmNpbmcgcGxhdGZvcm1zLCBiaW9pbmZvcm1hdGljIHBpbGVsaW5lcywgZXRjLiAKCjIuICoqcGVyZm9ybSBkYXRhLXdyYW5nbGluZyB3aXRoIHBoeWxvc2VxIGZ1bmN0aW9ucyAobW9zdGx5KSoqLiBUaGVyZSBhcmUgYSBudW1iZXIgb2YgYmFzaWMgb3BlcmF0aW9ucyB3aGljaCBzaG91bGQgYmUgcGVyZm9ybWVkOiAgCgogICAgKyByZW1vdmUgc2FtcGxlcyB3aXRoIGEgbG93IG51bWJlciBvZiBzZXF1ZW5jZXMgKGBtaW5zZXFgKSB1c2luZyB0aGUgcHJ1bmVfc2FtcGxlcygpIGZ1bmN0aW9ucyAoYW5kIHBlcmhhcHMgc2tpcCB0aGUgYW5hbHlzaXMgaWYgdGhlcmUgaXMgbGVzcyB0aGFuIG1pbl9zYW1wbGVzKS4gTm90ZSB0aGF0IHNhbXBsZSBhbmQgdGF4b25vbWljIGZpbHRlcmluZyBjYW4gYmUgYWxzbyBwZXJmb3JtZWQgd2l0aGluIHRoZSBgTmV0Q29NaTo6bmV0Q29uc3RydWN0KClgIGZ1bmN0aW9uCiAgCiAgICArIHJlbW92ZSBfRXVrYXJ5b3RhXyBhbmQgX0NobG9yb3BsYXN0c18gdXNpbmcgc3Vic2V0X3RheGEoKQogIAogICAgKyByZW1vdmUgdGF4YSB3aXRoIGFtYmlndW91cyBpZGVudGlmaWNhdGlvbiBvciBwb29yIGlkZW50aWZpY2F0aW9uIChzYXkgYXQgdGhlIGRvbWFpbiBvciBwaHlsdW0gbGV2ZWwpCiAgCiAgICArIHBlcmZvcm0gcHJldmFsZW5jZSBhbmQgYWJ1bmRhbmNlIGZpbHRlcmluZyAocHJldmFsZW5jZSBhYm92ZSBtaW5fcHJldiBBTkQgYWJ1bmRhbmNlIGFib3ZlIG1pbl9yZWxfYWIpLCBwb3NzaWJseSB1c2luZyBgZmlsdGVyX3RheGEoKWAKICAKICAgICsgcmV0dXJuIHNvbWUgc29ydCBvZiBzdW1tYXJ5IGZvciB3aGF0IGhhcyBiZWVuIGRvbmUgYW5kIHRoZSBlZmZlY3Qgb24gdGhlIG9yaWdpbmFsIHRhYmxlIChpbiB0ZXJtcyBvZiBsb3NzIG9mIHRheGEsIHNlcXVlbmNlcywgc2FtcGxlcyksIGluY2x1ZGluZyBzb21lIG1lYXN1cmUgb2YgZGl2ZXJzaXR5IGFuZCBldmVubmVzcyAgCiAgCjMuICoqcGVyZm9ybSBtaWNyb2JpYWwgYXNzb2NpYXRpb24gbmV0d29yayBpbmZlcmVuY2Ugd2l0aCBgbmV0Q29uc3RydWN0KClgKiogd2l0aCBzb21lIGVycm9yIHRyYXBwaW5nIGFuZCBzdG9yZSByZXN1bHRzIGluIGEgbGlzdC4gVGhpcyBleGFtcGxlIHVzZXMgdGhlIGZvbGxvd2luZyBpbmZlcmVuY2UgbWV0aG9kczogIAoKICAgICsgKipTcGFyQ0MqKiAoc3BhcmNjKCkgaW4gU3BpZWNFYXNpIHBhY2thZ2UpOiBuZWVkcyBoaWdoIG51bWJlciBvZiB0YXhhIGFuZCBzcGFyc2UgbWF0cml4ICAKICAgIAogICAgKyAqKkNDUkVQRSoqIChjY3JlcGUgcGFja2FnZSwgaXQgaXMgdGhlIG1haW4gYXBwcm9hY2ggdXNlZCBpbiBDb05ldCkgIAogICAgCiAgICArICoqU3BpZWNFYXNpKiogKFNwaWVjRWFzaSBwYWNrYWdlKSAgCiAgICAKICAgICsgKipTUFJJTkcqKiAoU1BSSU5HIHBhY2thZ2UpICAKCjQuICoqZ2V0IG5vZGVzLCBlZGdlcyBhbmQgbmV0d29yayBzdGF0cyoqOiB1c2UgTmV0Q29NaTo6bmV0QW5hbHl6ZSgpIHRvIGdldCBhbiBvYmplY3Qgd2l0aCBib3RoIG5vZGUgYW5kIG5ldHdvcmsgc3RhdHM7IHByaW50IGEgc3VtbWFyeSBmb3IgbmV0d29yayBzdGF0czsgYnVpbGQgYSB0aWR5Z3JhcGggb2JqZWN0IGZvciBmdXJ0aGVyIHN0YXRzOyBidWlsZCB0aWR5IGRhdGEgZnJhbWVzIHdpdGggcmVsZXZhbnQgc3RhdHM7IAoKVGhlIHNjcmlwdCBpcyBlc2lnbmVkIHRvIHNhdmUgZGF0YSBhbmQgYW1ueSBvZiB0aGUgcGxvdHMgaW4gYSBzdWJmb2xkZXIgKGhlcmUgIm5ldGNvbXBhcmVfb3V0cHV0IikuICAKCiMgVGhlIGFuYWx5c2lzLgoKIyMgU2V0dGluZyB0aGUgb3B0aW9ucy4gIAoKQSBudW1iZXIgb2Ygb3B0aW9ucyBwZXJ0YWluaW5nIHRvIHRoZSBhbmFseXNpcyBvciBybGF0ZWQgdG8gc2F2ZSBvcGVyYXRpb25zIGNhbiBiZSBzZXQgaW4gdGhlIGNodW5rIGJlbG93IGFuZCB3aWxsIGJlIHNhdmVkIGFuZCBwcmludGVkLiAKICAKYGBge3Igc2V0dGluZ19vcHRpb25zLCBlY2hvPUZBTFNFfQoKIyMjIHByb2Nlc3NpbmcgYW5kIGZvbGRlcnMgIyMjCiMgdGhlIGZvbGxvd2luZyBjb21tYW5kIGRldGVjdHMgdGhlIG51bWJlciBvZiBjb3JlcyBvbiBVTklYL01hY09TCm5jb3JlcyA8LSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMobG9naWNhbCA9IEYpICMgdG8gZGV0ZWN0IHBoeXNpY2FsIGNvcmVzIGluIE1hY09TCiMgdGhlIG5hbWUgb2YgdGhlIGZvbGRlciBjb250YWluaW5nIHRoZSBwaHlsb3NlcSBvYmplY3RzCmRhdGFfZm9sZGVyIDwtICJpbnB1dF9kYXRhIgojIHRoZSBuYW1lIG9mIHRoZSBmb2xkZXIgY29udGFpbmluZyBhZGRpdGlvbmFsIGZ1bmN0aW9ucyArIG9wdGlvbmFsIGRhdGEgKGxpa2UgdGhlIGxvb2t1cCB0YWJsZSkKc291cmNlX2ZvbGRlciA8LSAic291cmNlIgojIHRoZSBuYW1lIG9mIHRoZSBvdXRwdXQgZm9sZGVyCm91dHB1dF9mb2xkZXIgPC0gIm5ldGNvbXBhcmVfb3V0cHV0Igpwcm9jZXNzX2JhdGNoIDwtIFQKIyBpZiB0cnVlLCBhbGwgcGh5bG9zZXFzIGluIHRoZSBzdWJmb2xkZXIgcGh5c2Vxb2JqIHdpbGwgYmUgcHJvY2Vzc2VkIGluIGJhdGNoIChtYXkgdGFrZSB0aW1lKQoKIyBzb21lIGdyYXBoIG9wdGlvbnMsIHNoYXJlZCBldmVyeXdoZXJlCmRwaV9vcHRpb24gPC0gMzAwCmdfdHlwZSA8LSAidGlmZiIgCgojIHVzZSBhIGxvb2t1cCB0YWJsZSB0byBmaXggdGhlIGdlbmVyYSBhbmQgZmFtaWxpZXMgZm9yIExhY3RvYmFjaWxsYWxlcwp1c2VfbG9va3VwIDwtIFQKaWYodXNlX2xvb2t1cCl7CiAgbG9va3VwX3RhYmxlIDwtIHJlYWRfdHN2KGZpbGUucGF0aCgic291cmNlIiwgImxvb2t1cF9sYWN0b2JhY2lsbHVzLnR4dCIpLCBjb2xfdHlwZXMgPSAiY2NjY2NjIikKfQoKb3V0X2ZpbGVuYW1lX3ByZWYgPC0gIk1BTl9GTUJOX2dlbnVzXzMiICMgdGhlIHByZWZpeCB3aGljaCB3aWxsIGJlIHVzZWQgZm9yIGFsbCBvdXRwdXQgZmlsZW5hbWVzCgojIyMgZmlsdGVyaW5nIG9wdGlvbnMgIyMjCm1pbl9zYW1wbGVzIDwtIDIwCiMgdGhlIG1pbmltdW0gbnVtYmVyIG9mIHNhbXBsZXMgaW4gdGhlIGRhdGEgc2V0OiBzdHVkaWVzIHdpdGggPCBtaW5fc2FtcGxlcyBhZnRlciBmaWx0ZXJpbmcgd2lsbCBiZSBleGNsdWRlZCBmcm9tIHRoZSBhbmFseXNpcwptaW5fc2VxcyA8LSAxMDAwCiMgdGhlIG1pbmltdW0gbnVtYmVyIG9mIHNlcXVlbmNlcyBwZXIgc2FtcGxlCgojIHJlbW92ZXMgdW5jaGFyYWN0ZXJpemVkIHRheGEgKGkuZS4gdGF4YSBtaXNzaW5nIHRoZSBpZGVudGlmaWNhdGlvbiBhdCB0aGUgcGh5bHVtIGxldmVsIG9yIGFib3ZlKQpybV91bmNoYXIgPC0gVAojIHJlbW92ZSBFdWthcnlvdGEKcm1fZXVrIDwtIFQKIyByZW1vdmUgY2hsb3JvcGhhbHN0cyBhbmQgbWl0b2Nob25kcmlhCnJtX2NobG1pdCA8LSBUCiMgdGF4b25vbWljIGFnZ2xvbWVyYXRpb24gb3B0aW9uczogIm5vbmUiIG1lYW5zIG5vIGFnZ3JlZ2F0aW9uOyBpZiB0aGUgaW5wdXQgZGF0YSBhcmUgQVNWcyBvciBPVFVzIHRoZXkgd2lsbCBiZSB1c2VkIGFzIHN1Y2gKIyBpZiB0aGUgaW5wdXQgZmlsZSBpcyBmcm9tIEZNQk4gZGF0YSBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldHMgYXJlIGFnZ3JlZ2F0ZWQgYXQgZGlmZmVyZW50IHRheG9ub21pYyBsZXZlbHMuCiMgd2hlbiBjb21wYXJpbmcgZGF0YXNldHMgb2J0YWluZWQgdXNpbmcgZGlmZmVyZW50IGdlbmUgdGFyZ2V0cyBvciBwaXBlbGluZXMgc2hvdWxkIGJlIHNldCB0byAiZ2VudXMiCiMgIm5vbmUiLCAiZ2VudXMiLCBvciAiZmFtaWx5Iik7IGRvIG5vdCB1c2Ugc3BlY2llcywgd2lsbCBtb3N0IGxpa2VseSBjYXVzZSBlcnJvcnMKdGF4Z2xvbSA8LSAiZ2VudXMiICMgdGF4IGdsb20gd2lsbCBiZSBwZXJmb3JtZWQgYXQgdGhlIGdlbnVzIGxldmVsCiMgcmVtb3ZlIHRheGEgd2hpY2ggYXJlIHVuY2hhcmFjdGVyaXplZCBhdCB0aGUgZmFtaWx5LCBvcmRlciBhbmQgY2xhc3MgbGV2ZWwgKHNob3VsZCBub3QgYmUgdmVyeSBtYW55IGluIG1vc3QgZGF0YXNldHMpCmFib3ZlX2dlbnVzX2ZsYWcgPC0gVAojIHByZXZhbGVuY2UgYW5kIGFidW5kYW5jZSBmaWx0ZXIKIyBmbGFnIHRvIGRlY2lkZSBpZiBPVFUgc2hvdWxkIGJlIGZpbHRlcmVkLCBpLmUuIGlmIHRoZSBmaWx0ZXIgaXMgdG8gYmUgYXBwbGllZCBhdCBhbGwKZmlsdGVyT1RVcyA8LSBUUlVFCiMgZmxhZyB0byBzZXQgdGhlIGFwcGxpY2F0aW9uIG9mIGEgcHJldmFsZW5jZSBmaWx0ZXIKcHJldl9maWx0ZXIgPC0gVFJVRQojIHRoZSB0aHJlc2hvbGQgb2YgdGhlIGZpbHRlciwgYXMgZnJhY3Rpb24gb2Ygc2FtcGxlcwpwcmV2X3RocmVzaG9sZCA8LSAwLjA1CiMgdGhlIGFidW5kYW5jZSBhcyBmcmFjdGlvbiBvZiBzZXF1ZW5jZXMgKGluIGF0IGxlYXN0IG9uZSBzYW1wbGUpCmFiX3RocmVzaG9sZCA8LSAwLjAwNQpwYXNzX2JvdGggPC0gVFJVRSAKIyBpZiB0cnVlIGFuIE9UVSBtdXN0IHBhc3MgYm90aCBhYnVuZGFuY2UgYW5kIHByZXZhbGVuY2UgZmlsdGVyLCBvdGhlcndpc2UKIyBwYXNzaW5nIGVpdGhlciBpcyBlbm91Z2gKIyBvcHRpb25zIGZvciBzYXZpbmcgb3V0cHV0IG9mIHByZXZhbGVuY2UgYW5kIGFidW5kYW5jZSBmaWx0ZXIKc2F2ZV9wcmV2X2FiX3Bsb3QgPC0gVAojIGZsYWcgZm9yIHNhdmluZyB0aGUgcHJldiBhYiBwbG90CnByaW50X3ByZXZfYWJfcGxvdCA8LSBGCiMgZmxhZyBmb3IgcHJpbnRpbmcgdGhlIHBsb3QKc2F2ZV9wcmV2X3RhYmxlIDwtVCAKIyBmbGFnIGZvciBzYXZpbmcgdGhlIHByZXZhbGVuY2UgYW5kIGFidW5kYW5jZSB0YWJsZTsgc2hvdWxkIGJlIHNhdmVkIGJlY2F1c2UgaXQgY2FuIGJlIHVzZWQgbGF0ZXIKIyB0byBhZGQgaW5mbyB0byB0aGUgbm9kZSBzdGF0cwpzYXZlX3ByZXZfYWJfbGlzdCA8LVQKCiMgYSB2ZWN0b3Igd2l0aCB0aGUgbWV0aG9kcyB1c2VkIGZvciBpbmZlcmVuY2UsIHVzZSB0aGUgc3BlbGxpbmcgdXNlZCBpbiBgbmV0Q29uc3RydWN0KClgCiMgc2VlTmV0Q29NaSBkb2N1bWVudGF0aW9uIGZvciBhIGxzaXQgb2YgYXZhaWxhYmxlIG1ldGhvZHMgYW5kIG9wdGlvbnM7CiMgdGhlIGZpcnN0IG1ldGhvZCBpcyB0aGUgb25lIHdoaWNoIHdpbGwgYmUgdXNlZCB0byBkbyBjb21wYXJpc29ucyB3aXRoaW4gc2FtcGxlCiMgaWYgeW91IHdhbnQgdG8gcnVuIGp1c3Qgb25lIGxpc3Qgb25seSB0aGF0CgppbmZfbWV0aG9kcyA8LSBjKCJzcGllY2Vhc2kiLCAic3ByaW5nIiwgInNwYXJjYyIsICJjY3JlcGUiKQoKIyB0aGUgbGlzdCBvZiBwYXJhbWV0ZXJzIHRvIGJlIHBhc3NlZCBpbiBgbmV0Q29uc3RydWN0KClgIGZvciBlYWNoIG9mIHRoZSBtZXRob2RzOwojIHRoZXJlIG11c3QgYmUgb25lIGVudHJ5IGZvciBlYWNoIG9mIGhlIHBhcmFtZXRlcnMgaW4gdGhlIGxpc3QKIyBzb21lIHBhcmFtZXRlcnMgYXJlIG5vdCByZWFsbHkgbmVlZGVkIGFuZCB3aWxsIGJlIGlnbm9yZWQKaW5mX21ldGhvZHNfcGFyYW0gPC0gbGlzdCgKICBzcGllYWNlYXNpID0gbGlzdCgKICAgIHNwYXJNZXRob2QgPSAidC10ZXN0IiwKICAgIGFscGhhID0gMC4wMDEsCiAgICBtZWFzdXJlUGFyTGlzdCA9IGxpc3QoCiAgICAgIG1ldGhvZCA9ICdtYicsCiAgICAgIGxhbWJkYS5taW4ucmF0aW8gPSAxZS0yLAogICAgICBubGFtYmRhID0gMjAsCiAgICAgIHB1bHNhci5wYXJhbXMgPSBsaXN0KHJlcC5udW0gPQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDUwKQogICAgKSwKICAgIG5vcm1tZXRob2RQYXIgPSAibm9uZSIsCiAgICB6ZXJvbWV0aG9kUGFyID0gIm5vbmUiLAogICAgZGlzc0Z1bmNQYXIgPSAic2lnbmVkIiwKICAgIHZlcmJvc2VQYXIgPSAxCiAgKSwKICBzcHJpbmcgPSBsaXN0KAogICAgc3Bhck1ldGhvZCA9ICJ0LXRlc3QiLAogICAgYWxwaGEgPSAwLjAwMSwKICAgIG1lYXN1cmVQYXJMaXN0ID0gbGlzdChubGFtYmRhID0gNTAsIHJlcC5udW0gPSA1MCksCiAgICAgICAgbm9ybW1ldGhvZFBhciA9ICJub25lIiwKICAgIHplcm9tZXRob2RQYXIgPSAibm9uZSIsCiAgICBkaXNzRnVuY1BhciA9ICJzaWduZWQiLAogICAgdmVyYm9zZVBhciA9IDEKICApLAogIHNwYXJjYyA9IGxpc3QoCiAgICBzcGFyTWV0aG9kID0gInQtdGVzdCIsCiAgICBhbHBoYSA9IDAuMDAxLAogICAgbWVhc3VyZVBhckxpc3QgPSBsaXN0KGl0ZXIgPSAxMDAsIGlubmVyX2l0ID0gMjAsIHRoID0gMC4wNSksCiAgICBub3JtbWV0aG9kUGFyID0gIm5vbmUiLAogICAgemVyb21ldGhvZFBhciA9ICJub25lIiwKICAgIGRpc3NGdW5jUGFyID0gInNpZ25lZCIsCiAgICB2ZXJib3NlUGFyID0gMQogICksCiAgY2NyZXBlID0gbGlzdCgKICAgIHNwYXJNZXRob2QgPSAidC10ZXN0IiwKICAgIGFscGhhID0gMC4wMDEsCiAgICBtZWFzdXJlUGFyTGlzdCA9IE5VTEwsCiAgICBub3JtbWV0aG9kUGFyID0gImZyYWN0aW9ucyIsCiAgICB6ZXJvbWV0aG9kUGFyID0gIm5vbmUiLAogICAgZGlzc0Z1bmNQYXIgPSAic2lnbmVkIiwKICAgIHZlcmJvc2VQYXIgPSAxCiAgKQopCgojIG9wdGlvbnMgZm9yIG5ldHdvcmsgYW5hbHlzaXMKIyBkbyBWZW5uIHBsb3QKZG9fVmVubiA8LSBUCiMgY2FsY3VsYXRlcyBlZGdlIGJldHdlZW5uZXNzCmNhbGNfZV9iZXR3IDwtVAojIG1lcmdlIG5vZGUgc3RhdHMKbWVyZ2Vfbm9kZV9zdGF0cyA8LSBUCgojIGJ1aWxkIGFuZCBzYXZlIGEgbGlzdCB3aXRoIHRoZSBvcHRpb25zLCBmb3IgcmVwcm9kdWNpYmlsaXR5Cm9wdGlvbl9saXN0IDwtIGxpc3QoCiAgZ2VuZXJhbF9vcHRpb25zID0gbGlzdCgKICAgIG5jb3JlcyA9IG5jb3JlcywKICAgIGRhdGFfZm9sZGVyID0gZGF0YV9mb2xkZXIsCiAgICBzb3VyY2VfZm9sZGVyID0gc291cmNlX2ZvbGRlciwKICAgIG91dHB1dF9mb2xkZXIgPSBvdXRwdXRfZm9sZGVyLAogICAgcHJvY2Vzc19iYXRjaCA9IHByb2Nlc3NfYmF0Y2gsCiAgICB1c2VfbG9va3VwID0gdXNlX2xvb2t1cCwKICAgIG91dF9maWxlbmFtZXMgPSBvdXRfZmlsZW5hbWVfcHJlZiwKICAgIGdyZXMgPSBkcGlfb3B0aW9uLAogICAgZ3R5cGUgPSBnX3R5cGUKICApLAogIG1ldGhvZHNfb3B0aW9ucyA9IGxpc3QoCiAgICBtZXRob2RzID0gaW5mX21ldGhvZHMsCiAgICBwYXJhbWV0ZXJzID0gaW5mX21ldGhvZHNfcGFyYW0KICApLAogIGZpbHRlcmluZ19vcHRpb25zID0gbGlzdCgKICAgIG1pbl9zYW1wbGVzID0gbWluX3NhbXBsZXMsCiAgICBtaW5fc2VxcyA9IG1pbl9zZXFzLAogICAgZ2xvbV90YXhhID0gdGF4Z2xvbSwKICAgIHJtdW5jaGFyX2RwID0gcm1fdW5jaGFyLAogICAgcm1jaGxtaXQgPSBybV9jaGxtaXQsCiAgICBybWV1ayA9IHJtX2V1aywKICAgIHJtdW5jaGFyX2NvZiA9IGFib3ZlX2dlbnVzX2ZsYWcsCiAgICBmaWx0ZXJfT1RVcyA9IGZpbHRlck9UVXMsCiAgICBwcmV2ZmlsdGVyID0gcHJldl9maWx0ZXIsCiAgICBwcmV2dGhyZXNob2xkID0gcHJldl90aHJlc2hvbGQsCiAgICBhYnRocmVzaG9scyA9IGFiX3RocmVzaG9sZCwKICAgIHBhc3Nib3RoID0gcGFzc19ib3RoLAogICAgc2F2ZXByZXZhYnBsb3QgPSBzYXZlX3ByZXZfYWJfcGxvdCwgCiAgICBwcmludHByZXZhYnBsb3QgPSBwcmludF9wcmV2X2FiX3Bsb3QsCiAgICBzYXZlcHJldmFidGFibGUgPSBzYXZlX3ByZXZfdGFibGUsCiAgICBzYXZlX3ByZXZfYWJfbGlzdCA9IHNhdmVfcHJldl9hYl9saXN0CiAgKSwKICBuZXRzdGF0X29wdGlvbnMgPSBsaXN0KAogICAgZG9WZW5uID0gZG9fVmVubiwKICAgIGNhbGNFYmV0dyA9IGNhbGNfZV9iZXR3LAogICAgbWVyZ2VOc3RhdHMgPSBtZXJnZV9ub2RlX3N0YXRzCiAgKQopCgpzYXZlKG9wdGlvbl9saXN0LCBmaWxlID0gZmlsZS5wYXRoKG91dHB1dF9mb2xkZXIsIHBhc3RlKG91dF9maWxlbmFtZV9wcmVmLCJfb3B0aW9ucy5SZGF0YSIpKSkKCgpleHRfZnVuY3Rpb25zIDwtIHByaW50X2Z1bmN0aW9uX3JlcG9ydCgpCmlmKHZlcmJvc2Vfb3V0cHV0KSB7CiAgcHJpbnQoZXh0X2Z1bmN0aW9ucykKICBwcmludChvcHRpb25fbGlzdCkKICB9CgppZihwbGF5X2F1ZGlvKSBiZWVwKHNvdW5kID0gNikgIyBub3RpZmljYXRpb24KCgpgYGAKIyMgTG9hZGluZyB0aGUgb2JqZWN0cyB0byBwcm9jZXNzLiAgCgpUaGUgaW5wdXQgZGF0YSBhcmUgc3RvcmVkIGluIGEgZm9sZGVyIChpbnB1dF9kYXRhKSBhbmQgcHJvY2Vzc2VkIG9uZSBieSBvbmUgKGRlcGVuZGluZyBvbiB1c2VyIGlucHV0KSBvciBpbiBiYXRjaCAoc2VlIHByb2Nlc3NfYmF0Y2ggb3B0aW9uKS4gVGhlIGZvbGRlciBzaG91bGQgYWxzbyBjb250YWluIGEgLnRzdiB3aXRoIHN0dWR5IG1ldGFkYXRhLCB3aXRoIGEgbGFiZWwgdmFyaWFibGUgd2l0aCB0aGUgbnVtYmVyIG9mIHRoZSBzdHVkeSBhbmQgYW4gaW5kaWNhdG9yIG9uIHRoZSBuYXR1cmUgb2YgdGhlIGZpbGUgKEZNQk4gb3IsIGlmIHRoZSBhY2NuIG51bWJlciBpcyBpbiB0aGUgbmFtZSwgQVNWKS4gSGVyZSBJIGFtIGxvYWRpbmcgNSBzdHVkaWVzIGZyb20gRGFpcnlGTUJOIChTVDEwNiwgU1QxMTAsIFNUMTE1LCBTVDEzMSwgU1QxMzYpLiBUaGUgc3R1ZGllcyBkaWZmZXIgaW4gdGhlIHRhcmdldHMgKFNUMTA2IHRhcmdldHMgVjEtVjMgUk5BLCB0aGUgb3RoZXJzIHRoZSAxNlMgUk5BIGdlbmUsIFY0IG9yIFYzLVY0KSBhbmQgcGxhdGZvcm0uIExvb2sgYXQgdGhlIGV4YW1wbGUgZmlsZXMgdG8gc2VlIGhvdyB0aGUgc3R1ZHlfbWV0YWRhdGEgZmlsZSBzaG91bGQgYmUgc2V0dXAuICAgCgpgYGB7ciBsb2FkX2lucHV0X2RhdGEsIGVjaG8gPSBGQUxTRX0KCmlmKGtlZXBfdGltZSkgdGljKCJsb2FkaW5nIHBoeWxvc2VxIG9iamVjdHMiKQojIG9idGFpbiB0aGUgbGlzdCBvZiBmaWxlcyBpbiB0aGUgaW5wdXQgZm9sZGVyCmlucHV0X2ZpbGVfbGlzdCA8LSBsaXN0LmZpbGVzKGZpbGUucGF0aChkYXRhX2ZvbGRlcikpCiMgbWFuYWdlIC5yZGF0YSBmaWxlcyBmaXJzdCAoYnV0IHRoZXkgbXVzdCBiZSBwaHlsb3NlcSBvYmplY3RzIG9yIHdpbGwgYmUgZGVsZXRlZCBsYXRlcikKcmRhdGFfaW5wdXRfZmlsZV9saXN0IDwtIGlucHV0X2ZpbGVfbGlzdFtzdHJfZGV0ZWN0KGlucHV0X2ZpbGVfbGlzdCwgIi5yZGF0YXwuUmRhdGF8LlJEQVRBIildIAojIGdldCB0aGUgbGlzdCAuUmRhdGEKcmRhdGFfbmFtZXNfbGlzdCA8LSBzdHJfcmVtb3ZlKHJkYXRhX2lucHV0X2ZpbGVfbGlzdCwgIi5yZGF0YXwuUmRhdGF8LlJEQVRBIikKcmRhdGFfcGF0aF9saXN0IDwtIGZpbGUucGF0aChkYXRhX2ZvbGRlcixyZGF0YV9pbnB1dF9maWxlX2xpc3QpCiMgbG9hZCBhbmQgc2F2ZSBhcyAucmRzLCByZW1vdmUgb2JqZWN0cyBhbmQgZmlsZXMKZm9yIChpIGluIHNlcV9hbG9uZyhyZGF0YV9wYXRoX2xpc3QpKXsKICBsb2FkKHJkYXRhX3BhdGhfbGlzdFtpXSkgIyBsb2FkcyB0aGUgb2JqZWN0IGFzIHBoeXNlcWRhdGEKICBzYXZlUkRTKHBoeXNlcWRhdGEsIGZpbGUucGF0aChkYXRhX2ZvbGRlciwgcGFzdGUocmRhdGFfbmFtZXNfbGlzdFtpXSwiLnJkcyIsc2VwPSIiKSkpCiAgcm0ocGh5c2VxZGF0YSkKICBmaWxlLnJlbW92ZShyZGF0YV9wYXRoX2xpc3RbaV0pCn0KCiMgZ2V0IHRoZSBuYW1lcyBmb3IgdGhlIC5yZHMgZmlsZXMKCmlucHV0X2ZpbGVfbGlzdCA8LSBsaXN0LmZpbGVzKGZpbGUucGF0aChkYXRhX2ZvbGRlcikpCnJkc19pbnB1dF9maWxlX2xpc3QgPC0gaW5wdXRfZmlsZV9saXN0W3N0cl9kZXRlY3QoaW5wdXRfZmlsZV9saXN0LCAiLnJkc3wuUkRTIildIApuYW1lc19saXN0IDwtIHN0cl9yZW1vdmUocmRzX2lucHV0X2ZpbGVfbGlzdCwgIi5yZHN8LlJEUyIpCnJkc19wYXRoX2xpc3QgPC0gZmlsZS5wYXRoKGRhdGFfZm9sZGVyLHJkc19pbnB1dF9maWxlX2xpc3QpCiMgbG9hZCBhbGwgdGhlIGZpbGVzIGluIGEgbGlzdCB1c2luZyBhIGZ1bmN0aW9uYWwKcGh5c2VxX2xpc3QgPC0gbGFwcGx5KHJkc19wYXRoX2xpc3QsIHJlYWRSRFMpCiMgY2hlY2sgaWYgYWxsIGFyZSBwaHlsb3NlcSBvYmplY3RzCmlzX3BoeWxvc2VxIDwtIHNhcHBseShwaHlzZXFfbGlzdCwgZnVuY3Rpb24oeCkgY2xhc3MoeCk9PSJwaHlsb3NlcSIpCiMgY2xlYW4gdXAgdGhlIGxpc3QgaWYgbmVjZXNzYXJ5IGFuZCBnaXZlIG5hbWVzCnBoeXNlcV9saXN0IDwtIHBoeXNlcV9saXN0W2lzX3BoeWxvc2VxXQpuYW1lcyhwaHlzZXFfbGlzdCkgPC0gbmFtZXNfbGlzdFtpc19waHlsb3NlcV0KIyBpZiBwcm9jZXNzX2JhdGNoID09IEYgbmVlZCB0byBpbmRpY2F0ZSB3aGljaCBpcyB0byBiZSBwcm9jZXNzZWQ7IGRlZmF1bHRzIHRvIHRoZSBmaXJzdCBlbGVtZW50IG9mIHRoZSBsaXN0CiMgc28sIGl0IGlzIGVhc2llciBpZiB5b3UganVzdCBoYXZlIG9uZSBlbGVtZW50IGluIHRoZSBmb2xkZXIKaXRlbV90b19wcm9jZXNzIDwtIGl0ZW0gPC0gTkFfaW50ZWdlcl8KaWYoIXByb2Nlc3NfYmF0Y2gpIHtpdGVtIDwtIGlmZWxzZShpcy5uYShpdGVtX3RvX3Byb2Nlc3MpLCAxLCBpdGVtX3RvX3Byb2Nlc3MpfQppZighaXMubmEoaXRlbSkpewogIHBoeXNlcV9saXN0IDwtIHBoeXNlcV9saXN0W2l0ZW1dCiAgfQoKCiMgc2hvdWxkIGNoZWNrIGlmIHRoaXMgaXMgdG9vIGxhcmdlCiMgc2l6ZV9saW1pdCAKc2l6ZV9saW1pdCA8LSA1MDBlNkwKcGh5c2VxX3NpemUgPC0gb2JqX3NpemUocGh5c2VxX2xpc3QpCnNpemVfd2FybmluZyA8LSBpZmVsc2UoYXMubnVtZXJpYyhwaHlzZXFfc2l6ZSk+c2l6ZV9saW1pdCwKICAgICAgICAgICAgICAgICAgICAgICAiV0FSTklORzogdG9vIG11Y2ggZGF0YSB0byBwcm9jZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgib2JqZWN0IHNpemUiLCBhcy5jaGFyYWN0ZXIocGh5c2VxX3NpemUpLCAiIEIiLCBzZXAgPSAiICIpKSAKIyBsb2FkIHRoZSBzdHVkeSBtZXRhZGF0YSAoaWYgbm90IGF2YWlsYWJsZSBvciBub3QgcHJvcGVybHkgZm9ybWF0dGVkIHRoZSBzY3JpcHQgd2lsbCBjcmVhdGUgYSBzdWl0YWJsZSBkYXRhIGZyYW1lKQojIG5vIGlucHV0IHJlcXVpcmVkLCBzaG91bGQgd29yayB3aXRoIGRlZmF1bHRzCnN0dWR5X21ldGFkYXRhIDwtIGxvYWRfbWV0YWRhdGEoKQojIHByaW50IHRoZSBzdHVkeSBtZXRhZGF0YQpzdHVkeV9tZXRhZGF0YSAlPiUgCiAgZHBseXI6OnNlbGVjdChsYWJlbDpzdHVkeUlkLCBzYW1wbGVzLCBzaG9ydF9kZXNjciwgdHlwZSwgcmVmX3Nob3J0KQoKbG9hZGluZ19yZXBvcnQgPC0gdGliYmxlKCBvYmplY3RzX3RvX3Byb2Nlc3MgPSBjKG5hbWVzX2xpc3Qsc2l6ZV93YXJuaW5nKSkKaWYodmVyYm9zZV9vdXRwdXQpIHByaW50KGxvYWRpbmdfcmVwb3J0KQppZihwbGF5X2F1ZGlvKSBiZWVwKHNvdW5kID0gNikKaWYoa2VlcF90aW1lKSB0b2MoKQoKYGBgCgojIyBGaWx0ZXJpbmcuICAKCkZpbHRlcmluZyBvcHRpb25hbGx5IGZpbHRlcnMgc2FtcGxlcyBhbmQgdGF4YSAob24gdGhlIGJhc2lzIG9mIHRheG9ub215IGFuZCBvZiBhIHByZXZhbGVuY2UgYW5kIGFidW5kYW5jZSBmaWx0ZXIpIGFuZCByZXR1cm5zIHRoZSBmaWx0ZXJlZCBgcGh5bG9zZXFgIG9iamVjdHMgaW4gYSBzZXBhcmF0ZSBsaXN0LiBUYXhvbm9taWMgYWdnbG9tZXJhdGlvbiBpcyBvcHRpb25hbGx5IGNhcnJpZWQgb3V0LiBUaGUgb3B0aW9ucyBmb3IgZmlsdGVyaW5nIGFyZSB0aG9zZSBkZWZpbmVkIGluIHRoZSBvcHRpb25zIHNlY3Rpb24uICAKCiMjIyBGaWx0ZXIgc2FtcGxlcy4gIAoKU2FtcGxlcyB3aXRoIGxlc3MgdGhhbiBhIGdpdmVuIG51bWJlciBvZiBzZXF1ZW5jZXMgYXJlIGRpc2NhcmRlZCBmaXJzdC4gQXMgYSBjb25zZXF1ZW5jZSwgYSBnaXZlbiBgcGh5bG9zZXFgIG9iamVjdCBtYXkgZmFsbCBvdXQgYmVsb3cgdGhlIHNldCBtaW5pbXVtIG9mIHNhbXBsZXMuIFRoZXJlZm9yZSB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgcGVyIG9iamVjdCBpcyBjaGVja2VkIGFnYWluLgoKYGBge3IgZmlsdGVyX2FuZF9nbG9tX3NhbXBsZXMsIGRwaSA9IDk2fQppZihrZWVwX3RpbWUpIHRpYygiZmlsdGVyIGFuZCBnbG9tIHNhbXBsZXMiKQojIGZpcnN0IHBydW5lIG9iamVjdHMgd2l0aCBsZXNzIHRoYW4gbWluX3NlcXMgc2VxdWVuY2VzCnBoeXNlcV9saXN0XzAgIDwtIHBoeXNlcV9saXN0CiMgY3JlYXRlIGZpcnN0IHN0ZXAgb2YgdGhlIGZpbHRlcmluZyByZXBvcnQKZmlsdGVyaW5nX3JlcG9ydCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGh5c2VxX2xpc3QpKQpmaWx0ZXJpbmdfcmVwb3J0XzAgPC0gbWFwKHBoeXNlcV9saXN0LCByZXBvcnRfc3RlcF8wKQpmb3IoaSBpbiBzZXFfYWxvbmcocGh5c2VxX2xpc3QpKXsKICBjYXQoInByb2Nlc3NpbmcgIiwgaSwgIiBvZiAiLCBsZW5ndGgocGh5c2VxX2xpc3QpLCAiXG4iKQogICMgc2V0IHBsb3RfZWNkZj1GIGZvciBmYXN0ZXIgZXhlY3V0aW9uCiAgcGh5c2VxX2xpc3RfMFtbaV1dIDwtIHBydW5lX3NhbXBsZXNfYnlfc2l6ZShwaHlzZXFfbGlzdFtbaV1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMocGh5c2VxX2xpc3QpW2ldLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfZWNkZiA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluc2VxcyA9IG1pbl9zZXFzKQp9CmNhdCgiLi4uZG9uZSBwcnVuaW5nIHNhbXBsZXMuLi5cbiIpCiMgbm93IHJlY2hlY2sgaWYgYWxsIG9iamVjdHMgaGF2ZSBlbm91Z2ggc2FtcGxlcwpwaHlzZXFfbGlzdF8xIDwtIHJlbV9sb3dfc2FtcGxlX29iaihwaHlzZXFfbGlzdF8wLCBtcyA9IG1pbl9zYW1wbGVzKQojIGNyZWF0ZSBzZWNvbmQgc3RlcCBvZiB0aGUgZmlsdGVyaW5nIHJlcG9ydApzdGFnZV9uYW1lID0gInBydW5lIHNhbXBsZXMiCmZvcihpIGluIHNlcV9hbG9uZyhwaHlzZXFfbGlzdF8wKSl7CiAgaWYobmFtZXMocGh5c2VxX2xpc3RfMClbaV0gJWluJSBuYW1lcyhwaHlzZXFfbGlzdF8xKSl7CiAgICBuYW1lIDwtIG5hbWVzKHBoeXNlcV9saXN0XzApW2ldCiAgICBzdGFnZSA8LSByZXBvcnRfc3RlcF9uKG15X3BoeXNlcSA9IHBoeXNlcV9saXN0XzFbW25hbWVdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG15X3BoeXNlcV9vID0gcGh5c2VxX2xpc3RfMFtbaV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhZ2VfbmFtZSA9IHN0YWdlX25hbWUpIAogIH0gZWxzZSB7CiAgICBzdGFnZSA8LSBjKHN0YWdlX25hbWUsCiAgICAgICAgICAgICAgc2FtcGxlcyA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHNlcXVlbmNlcyA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHRheGEgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBwcm9wX3NhbXBsZXMgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBwcm9wX3NlcSA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHByb3BfdGF4YSA9IE5BX3JlYWxfKQogIH0KICBmaWx0ZXJpbmdfcmVwb3J0W1tpXV0gPC0gcmJpbmQoc3RhZ2VfMCA9IGFzLmNoYXJhY3RlcihmaWx0ZXJpbmdfcmVwb3J0XzBbW2ldXSksIHN0YWdlXzEgPXN0YWdlKQogIG5hbWVzKGZpbHRlcmluZ19yZXBvcnQpW2ldIDwtIG5hbWVzKHBoeXNlcV9saXN0XzApW2ldCn0KCgppZih2ZXJib3NlX291dHB1dCkgcHJpbnQoZmlsdGVyaW5nX3JlcG9ydCkKaWYocGxheV9hdWRpbykgYmVlcChzb3VuZCA9IDYpCmlmKGtlZXBfdGltZSkgdG9jKCkKYGBgCgojIyBUYXhvbm9taWMgZmlsdGVyaW5nLiAgCgpBIG51bWJlciBvZiB0YXhvbm9taWMgZmlsdGVyaW5nIG9wZXJhdGlvbnMgaXMgcGVyZm9ybWVkIGF0IHRoaXMgc3RhZ2UgKGRlcGVuZGluZyBvbiB0aGUgbmF0dXJlIG9mIHRoZSBvYmplY3QgYW5kIGZpbHRlcmluZyBvcHRpb25zLCBzZXQgaW4gdGhlIHNldHRpbmdfb3B0aW9ucyBjaHVuaykuIFBsb3RzIGFyZSBvcHRpb25hbGx5IHByb2R1Y2VkIGFuZCBzYXZlZC4gIAoKYGBge3IgdGF4b25vbWljX2ZpbHRlcmluZywgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJ0YXhvbm9taWMgZmlsdGVyaW5nLCBzdGVwIDEiKQoKIyBjaGVjayB0aGUgbmF0dXJlIG9mIHRoZSB0YXhvbm9taWMgdGFibGUKIyBpbiBGTUJOIHlvdSBlaXRoZXIgaGF2ZSBBU1YgdGFibGVzIG9yIHRheCB0YWJsZXMgd2l0aCBhZ2dsb21lcmF0aW9uIGF0IHRoZSBzcGVjaWVzIGxldmVsIG9yIGFib3ZlCiMgd2hpY2ggYXJlIHRoZSBuYW1lcyBvZiB0aGUgdGF4IGxldmVscz8KdGF4X2xldmVscyA8LSBjKCJkb21haW4iLCJwaHlsdW0iLCJjbGFzcyIsIm9yZGVyIiwiZmFtaWx5IiwiZ2VudXMiLCJzcGVjaWVzIikKIyBnZXQvc2V0IHJhbmsgbmFtZXMgKHRoaXMgaXMgbmVjZXNzYXJ5IGJlY2F1c2UgcGh5bG9zZXEgb2JqZWN0cyBmcm9tIEZNQk4gb3IgZnJvbSB0aGUKIyBiaW9jb25kdWN0b3IgcGlwZWxpbmUgd2l0aCBTSUxWQSBoYXZlIGRpZmZlcmVudCBuYW1lcykKCnBoeXNlcV9saXN0XzIgPC0gbGFwcGx5KHBoeXNlcV9saXN0XzEsIGdzZXRfcmFua19uYW1lcywgdGF4X2xldmVscyA9IHRheF9sZXZlbHMpCiMgcmVtb3ZlIHVuY2hhcmFjdGVyaXplZCB0YXhhIGluIGFsbCBwaHlsb3NlcSBvYmplY3RzICh1c2luZyBhIGZ1bmN0aW9uYWwpCnBoeXNlcV9saXN0XzMgPC0gcGh5c2VxX2xpc3RfMgppZihybV91bmNoYXIpewogIHBoeXNlcV9saXN0XzMgPC0gbGFwcGx5KHBoeXNlcV9saXN0XzIsIHN1YnNldF90YXhhLCAhaXMubmEoZG9tYWluKSAmICFkb21haW4gJWluJSBjKCIiLCAidW5jaGFyYWN0ZXJpemVkIikpCiAgcGh5c2VxX2xpc3RfMyA8LSBsYXBwbHkocGh5c2VxX2xpc3RfMywgc3Vic2V0X3RheGEsICFpcy5uYShwaHlsdW0pICYgIXBoeWx1bSAlaW4lIGMoIiIsICJ1bmNoYXJhY3Rlcml6ZWQiKSkKfQoKIyBvcHRpb25hbGx5IHJlbW92ZSBFdWthcnlvdGVzCmlmKHJtX2V1aykgewogIHBoeXNlcV9saXN0XzMgPC0gbGFwcGx5KHBoeXNlcV9saXN0XzMsIHN1YnNldF90YXhhLCBkb21haW4gIT0iRXVrYXJ5b3RhIikKfQoKIyBvcHRpb25hbGx5IHJlbW92ZSBjaGxvcm9wbGFzdHMgYW5kIG1pdG9jaG9uZHJpYSAKaWYocm1fY2hsbWl0KSB7CiAgcGh5c2VxX2xpc3RfMyA8LSBsYXBwbHkocGh5c2VxX2xpc3RfMywgcmVtb3ZlX0NobF9NaXQpCn0KCiMgdXNlIGxvb2t1cCB0YWJsZSB0byBjaGFuZ2UgdGF4b25vbXkgb2YgTGFjdG9iYWNpbGx1czsgbmVjZXNzYXJ5IGZvciBwaHlsb3NlcSBvYmplY3RzIHByb2R1Y2VkIHdpdGggU0lMVkEKIyBidXQgbm90IGZvciBvYmplY3RzIGV4dHJhY3RlZCBmcm9tIEZNQk4gKHdoaWNoIGFyZSB0cmFuc2Zvcm1lZCBiZWZvcmUgZXh0cmFjdGlvbik7IHdpbGwgYWxzbyBjaGFuZ2UgdGhlIAojIHRheGEgbmFtZXMgZm9yIEFTVnMKIyBNQVkgQkUgU0xPVwppZih1c2VfbG9va3VwKXsKICAjIGxvb3Agb3ZlciB0aGUgbGlzdCBvZiBwaHlsb3NlcSBvYmplY3RzCiAgZm9yKGkgaW4gc2VxX2Fsb25nKHBoeXNlcV9saXN0XzMpKXsKICAgICMgY2hlY2sgdGhlIGxlbmd0aCBvZiB0aGUgbmFtZXMgb2YgdGhlIHRheGE7IGlmIDwxMDAgaXQgaXMgZnJvbSBGTUJOLCBicmVhawogICAgaWYobWVhbihzYXBwbHkodGF4YV9uYW1lcyhwaHlzZXFfbGlzdF8zW1tpXV0pLCBuY2hhciksbmEucm0gPSBUKTwxMDApewogICAgICBuZXh0CiAgICB9IGVsc2UgewogICAgICAjIGNoYW5nZSB0aGUgbmFtZXMKICAgICAgdG5hbWVzIDwtIHN0cl9jKCJBU1YiLHNlcSgxOm50YXhhKHBoeXNlcV9saXN0XzNbW2ldXSkpKQogICAgICB0YXhhX25hbWVzKHBoeXNlcV9saXN0XzNbW2ldXSk8LXRuYW1lcwogICAgICAjIGdldCBzcGVjaWVzIHRvIGNoYW5nZQogICAgICB0YXhhX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoYXModGF4X3RhYmxlKHBoeXNlcV9saXN0XzNbW2ldXSksIm1hdHJpeCIpKQogICAgICB0YXhhX3RhYmxlIDwtIHRheGFfdGFibGUgJT4lIG11dGF0ZShpZCA9IHN0cl9jKGdlbnVzLCBzcGVjaWVzLCBzZXAgPSAiICIpKQogICAgICB0YXhhX3RhYmxlIDwtIGxlZnRfam9pbih0YXhhX3RhYmxlLCBsb29rdXBfdGFibGUpCiAgICAgIG5fY2hhbmdlcyA8LSBzdW0oIWlzLm5hKHRheGFfdGFibGUkbmV3X3NwZWNpZXMpKQogICAgICB0YXhhX3RhYmxlIDwtIHRheGFfdGFibGUgJT4lIG11dGF0ZShzcGVjaWVzID0gaWZfZWxzZSghaXMubmEobmV3X3NwZWNpZXMpLCBuZXdfc3BlY2llcywgc3BlY2llcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbnVzID0gaWZfZWxzZSghaXMubmEobmV3X2dlbnVzKSwgbmV3X2dlbnVzLCBnZW51cykKICAgICAgKQogICAgICAjIHJlbW92ZSBjb2x1bW5zCiAgICAgIHRheGFfdGFibGUgPC0gZHBseXI6OnNlbGVjdCh0YXhhX3RhYmxlLCBkb21haW46c3BlY2llcykKICAgICAgIyBub3cgY2hhbmdlIGdlbnVzIGZvciBMYWN0b2JhY2lsbHVzIHdpdGggbm8gc3BlY2llcwogICAgICB0b19jaGFuZ2VfbGIgPC0gd2hpY2goKHRheGFfdGFibGUkZ2VudXMgPT0gIkxhY3RvYmFjaWxsdXMiKSAmIGlzLm5hKHRheGFfdGFibGUkc3BlY2llcykpCiAgICAgIHRheGFfdGFibGUkZ2VudXNbdG9fY2hhbmdlX2xiXTwtIkxhY3RvYmFjaWxsdXMgY29tcGxleCIKICAgICAgIyByZXBsYWNlIExldWNvbm9zdG9jYWNlYWUgd2l0aCBMYWN0b2JhY2lsbGFjZWFlCiAgICAgIG5fbGV1YyA8LSBucm93KGRwbHlyOjpmaWx0ZXIodGF4YV90YWJsZSwgZmFtaWx5ID09ICJMZXVjb25vc3RvY2FjZWFlIikpCiAgICAgIHRheGFfdGFibGUgPC0gdGF4YV90YWJsZSAlPiUgbXV0YXRlKGZhbWlseSA9IGlmZWxzZShmYW1pbHkgPT0gIkxldWNvbm9zdG9jYWNlYWUiLCAiTGFjdG9iYWNpbGxhY2VhZSIsIGZhbWlseSkpCiAgICAgIHRheGFfdGFibGUgPC0gYXMubWF0cml4KHRheGFfdGFibGUpCiAgICAgIHJvd25hbWVzKHRheGFfdGFibGUpPC10bmFtZXMKICAgICAgdGF4X3RhYmxlKHBoeXNlcV9saXN0XzNbW2ldXSk8LXRheGFfdGFibGUKICAgICAgaWYodmVyYm9zZV9vdXRwdXQpewogICAgICAgIGNhdChuYW1lcyhwaHlzZXFfbGlzdF8zKVtpXSwiOiBjaGFuZ2VkICIsIG5fY2hhbmdlcytuX2xldWMsICIgdGF4YVxuIiwgc2VwID0iIikKICAgICAgfQogICAgfQogIH0KfQoKIyByZW1vdmUgZnVydGhlciB0YXhhIHdoaWNoIGFyZSB1bmNoYXJhY3Rlcml6ZWQgYXQgdGhlIGZhbWlseSB0byBjbGFzcyBsZXZlbAoKIyBub3RlIGZvciBzZWxmOiBtaWdodCBpbXByb3ZlIGl0IGJ5IHNldHRpbmcgdGhlIGxldmVsIGF0IG9yIGFib3ZlIHdoaWNoIHVuY2hhcmFjdGVyaXplZCB0YXhhIGNhbiBiZSByZW1vdmVkCiMgcmF0aGVyIHRoYW4gdXNpbmcgYSBUL0YgZmxhZwppZihhYm92ZV9nZW51c19mbGFnKXsKICBwaHlzZXFfbGlzdF8zIDwtIGxhcHBseShwaHlzZXFfbGlzdF8zLCBzdWJzZXRfdGF4YSwgIWlzLm5hKGNsYXNzKSAmICFjbGFzcyAlaW4lIGMoIiIsICJ1bmNoYXJhY3Rlcml6ZWQiKSkgIyBDbGFzcwogIHBoeXNlcV9saXN0XzMgPC0gbGFwcGx5KHBoeXNlcV9saXN0XzMsIHN1YnNldF90YXhhLCAhaXMubmEob3JkZXIpICYgIW9yZGVyICVpbiUgYygiIiwgInVuY2hhcmFjdGVyaXplZCIpKSAjIE9yZGVyCiAgcGh5c2VxX2xpc3RfMyA8LSBsYXBwbHkocGh5c2VxX2xpc3RfMywgc3Vic2V0X3RheGEsICFpcy5uYShmYW1pbHkpICYgIWZhbWlseSAlaW4lIGMoIiIsICJ1bmNoYXJhY3Rlcml6ZWQiKSkgIyBGYW1pbHkKfQoKIyBvcHRpb25hbGx5IHBlcmZvcm0gdGF4b25vbWljIGFnZ2xvbWVyYXRpb24sIAoKIyBtYXkgdGFrZSBzb21lIHRpbWU7IGNhbiBiZSBtYWRlIGZhc3RlciB3aXRoIHBseXIgZnVuY3Rpb25zIHVzaW5nIHBhcmFsbGVsaXphdGlvbiBvciB3aXRoIGZ1cnJyCnBoeXNlcV9saXN0XzQgPC0gcGh5c2VxX2xpc3RfMwppZih0YXhnbG9tICE9ICJub25lIil7CiAgcGh5c2VxX2xpc3RfNCA8LSBsYXBwbHkocGh5c2VxX2xpc3RfMywgdGF4X2dsb21fbmFtZV9jaGFuZ2UsIHRheGFfZ2xvbSA9IHRheGdsb20pCn0KCiMgY3JlYXRlIHRoaXJkIHN0ZXAgb2YgdGhlIGZpbHRlcmluZyByZXBvcnQKc3RhZ2VfbmFtZSA9ICJ0YXhvbm9taWMgZmlsdGVyK2dsb20iCmZvcihpIGluIHNlcV9hbG9uZyhwaHlzZXFfbGlzdF8wKSl7CiAgaWYobmFtZXMocGh5c2VxX2xpc3RfMClbaV0gJWluJSBuYW1lcyhwaHlzZXFfbGlzdF8xKSl7CiAgICBuYW1lIDwtIG5hbWVzKHBoeXNlcV9saXN0XzApW2ldCiAgICBzdGFnZSA8LSByZXBvcnRfc3RlcF9uKG15X3BoeXNlcSA9IHBoeXNlcV9saXN0XzRbW25hbWVdXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG15X3BoeXNlcV9vID0gcGh5c2VxX2xpc3RfMFtbaV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhZ2VfbmFtZSA9IHN0YWdlX25hbWUpIAogIH0gZWxzZSB7CiAgICBzdGFnZSA8LSBjKHN0YWdlX25hbWUsCiAgICAgICAgICAgICAgc2FtcGxlcyA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHNlcXVlbmNlcyA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHRheGEgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBwcm9wX3NhbXBsZXMgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBwcm9wX3NlcSA9IE5BX3JlYWxfLAogICAgICAgICAgICAgIHByb3BfdGF4YSA9IE5BX3JlYWxfKQogIH0KICBmaWx0ZXJpbmdfcmVwb3J0W1tpXV0gPC0gcmJpbmQoZmlsdGVyaW5nX3JlcG9ydFtbaV1dLCBzdGFnZV8zID1zdGFnZSkKfQoKaWYoa2VlcF90aW1lKSB0b2MoKQoKaWYoa2VlcF90aW1lKSB0aWMoImNhbGN1bGF0ZSBkaXZlcnNpdHkgcHJlLWZpbHRlciIpCgojIENhbGN1bGF0ZSBkaXZlcnNpdHkgcHJpb3IgdG8gZmlsdGVyaW5nIGZvciBwcmV2YWxlbmNlIGFuZCBhYnVuZGFuY2UKCmRpdl9lc3RfcHJlZmlsdGVyIDwtIG1hcF9kZnIocGh5c2VxX2xpc3RfNCwgcGh5bG9zZXE6OmVzdGltYXRlX3JpY2huZXNzLCBzcGxpdCA9IEYsIG1lYXN1cmU9YygiT2JzZXJ2ZWQiLCJDaGFvMSIsIlNoYW5ub24iKSkKZGl2X2VzdF9wcmVmaWx0ZXIgPC0gbXV0YXRlKGRpdl9lc3RfcHJlZmlsdGVyLCBQaWVsb3VfSiA9IFNoYW5ub24vbG9nKE9ic2VydmVkKSkKIyBjYWxjdWxhdGUgYW5kIGFkZCBhdmUgQnJheS1DdXJ0aXMgZGlzc2ltaWxhcml0eQptZWFuYmNkaXN0IDwtIG1hcChwaHlzZXFfbGlzdF80LCBwaHlsb3NlcTo6ZGlzdGFuY2UsIG1ldGhvZD0iYnJheSIpCmRpdl9lc3RfcHJlZmlsdGVyJGF2ZV9CQyA8LSB1bmxpc3QobWFwKG1lYW5iY2Rpc3QsIG1lYW4pKQoKcm93Lm5hbWVzKGRpdl9lc3RfcHJlZmlsdGVyKSA8LSBuYW1lcyhwaHlzZXFfbGlzdF80KQojIHNhdmUgZm9yIGZ1cnRoZXIgdXNlCnNhdmUoZGl2X2VzdF9wcmVmaWx0ZXIsIAogICAgIGZpbGUgPSBwYXN0ZShmaWxlLnBhdGgob3V0cHV0X2ZvbGRlcixvdXRfZmlsZW5hbWVfcHJlZiksICJfZGl2cHJlZmlsdGVyLlJkYXRhIixzZXA9IiIpKQoKaWYoa2VlcF90aW1lKSB0b2MoKQoKIyBQcmV2YWxlbmNlIGFuZCBhYnVuZGFuY2UgZmlsdGVyCmlmKGtlZXBfdGltZSkgdGljKCJ0YXhvbm9taWMgZmlsdGVyaW5nLCBzdGVwIDIiKQoKIyBOT1RFIHVzaW5nIGEgcHJldmFsZW5jZSBmaWx0ZXIgYmFzZWQgb24gZnJhY3Rpb24gbWF5IGJlIHdyb25nIGZvciBzdHVkaWVzIHdpdGggbGFyZ2UgCiMgbnVtYmVyIG9mIHNhbXBsZXMsIGluIHdoaWNoIG9uZSBtaWdodCB3YW50IHRvIHJldGFpbiB0YXhhIHdoaWNoIGFwcGVhciBpbiA+MTAgc2FtcGxlcyAKCnBoeXNlcV9saXN0XzUgPC0gcGh5c2VxX2xpc3RfNAojIHdpbGwgYmUgc2tpcHBlZCBpZiBmaWx0ZXJPVFVzID09IEYKbm9kZV9zdGF0X2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBoeXNlcV9saXN0XzUpKQojIGEgbGlzdCB3aGljaCB3aWxsIGhvc3Qgbm9kZSBzdGF0cywgbGlrZSB0aGUgcHJldmFiIGRhdGEsIGluaXRpYWxseSBlbXB0eSwKIyBpZiBub3RoaW5nIGlzIGFkZGVkIGF0IHRoaXMgc3RhZ2UsIG5vZGUgc3RhdHMgd2lsbCBiZSBhZGRlZCBhdCBhIGxhdGVyIHN0YWdlCmlmKGZpbHRlck9UVXMpewogIHByZXZfYWJfbGlzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGh5c2VxX2xpc3RfNCkpCiAgIyB3aWxsIGhvc3QgdGhlIGxpc3RzIHdpdGggdGhlIHJlc3VsdHMKICBmb3IoaSBpbiBzZXFfYWxvbmcocGh5c2VxX2xpc3RfNCkpewogICAgaWYodmVyYm9zZV9vdXRwdXQpIGNhdCgicHJldmFsZW5jZSBhbmQgYWJ1bmRhbmNlIGZpbHRlciwgcGh5c2VxICIsaSwiIG9mICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aChwaHlzZXFfbGlzdF80KSwiXG4iKQogICAgcHJldl9hYl9saXN0W1tpXV0gPC0gZmlsdGVyX2J5X3ByZXZfYWIoCiAgICAgIG15cGh5c2VxID0gcGh5c2VxX2xpc3RfNFtbaV1dLAogICAgICBuYW1lID0gbmFtZXMocGh5c2VxX2xpc3RfNClbaV0KICAgICkKICAgIG5hbWVzKHByZXZfYWJfbGlzdClbaV08LW5hbWVzKHBoeXNlcV9saXN0XzQpW2ldCiAgICAjIHNhdmUgdGhlIHByb2Nlc3NlZCBwaHlsb3NlcQogICAgcGh5c2VxX2xpc3RfNVtbaV1dIDwtIHByZXZfYWJfbGlzdFtbaV1dW1sxXV0KICAgICMgc2F2ZSBwcmV2IGFiIHRhYmxlCiAgICBub2RlX3N0YXRfbGlzdFtbaV1dIDwtIHByZXZfYWJfbGlzdFtbaV1dW1s0XV0KICAgIG5hbWVzKG5vZGVfc3RhdF9saXN0KVtpXSA8LSBuYW1lcyhwaHlzZXFfbGlzdF80KVtpXQogIH0KfQojIG9wdGlvbmFsbHkgc2F2ZSB0aGUgcHJldl9hYl9saXN0CmlmKHNhdmVfcHJldl9hYl9saXN0KSBzYXZlKHByZXZfYWJfbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZShmaWxlLnBhdGgob3V0cHV0X2ZvbGRlcixvdXRfZmlsZW5hbWVfcHJlZiksIG5hbWUsICJfcHJldmFibC5SZGF0YSIsc2VwPSIiKSkKCiMgY3JlYXRlIGZvdXJ0aCBzdGVwIG9mIHRoZSBmaWx0ZXJpbmcgcmVwb3J0CnN0YWdlX25hbWUgPSAicHJldmFsZW5jZSBhbmQgYWJ1bmRhbmNlIgpmb3IoaSBpbiBzZXFfYWxvbmcocGh5c2VxX2xpc3RfMCkpewogIGlmKG5hbWVzKHBoeXNlcV9saXN0XzApW2ldICVpbiUgbmFtZXMocGh5c2VxX2xpc3RfMSkpewogICAgbmFtZSA8LSBuYW1lcyhwaHlzZXFfbGlzdF8wKVtpXQogICAgc3RhZ2UgPC0gcmVwb3J0X3N0ZXBfbihteV9waHlzZXEgPSBwaHlzZXFfbGlzdF81W1tuYW1lXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICBteV9waHlzZXFfbyA9IHBoeXNlcV9saXN0XzBbW2ldXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YWdlX25hbWUgPSBzdGFnZV9uYW1lKSAKICB9IGVsc2UgewogICAgc3RhZ2UgPC0gYyhzdGFnZV9uYW1lLAogICAgICAgICAgICAgIHNhbXBsZXMgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBzZXF1ZW5jZXMgPSBOQV9yZWFsXywKICAgICAgICAgICAgICB0YXhhID0gTkFfcmVhbF8sCiAgICAgICAgICAgICAgcHJvcF9zYW1wbGVzID0gTkFfcmVhbF8sCiAgICAgICAgICAgICAgcHJvcF9zZXEgPSBOQV9yZWFsXywKICAgICAgICAgICAgICBwcm9wX3RheGEgPSBOQV9yZWFsXykKICB9CiAgZmlsdGVyaW5nX3JlcG9ydFtbaV1dIDwtIHJiaW5kKGZpbHRlcmluZ19yZXBvcnRbW2ldXSwgc3RhZ2VfNCA9c3RhZ2UpCn0KCiMgcmVtb3ZlIHVubmVlZGVkIG9iamVjdHMKcm0ocGh5c2VxX2xpc3RfMSwgcGh5c2VxX2xpc3RfMywgcGh5c2VxX2xpc3RfNCwgcHJldl9hYl9saXN0LCB0YXhhX3RhYmxlKQojIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgcmVwb3J0CmZpbHRlcmluZ19yZXBvcnRfZGYgPC0gbWFwX2RmcihmaWx0ZXJpbmdfcmVwb3J0LCBhcy5kYXRhLmZyYW1lLCAuaWQgPSAiZGF0YXNldCIpCmZpbHRlcmluZ19yZXBvcnRfZGYgPC0gZmlsdGVyaW5nX3JlcG9ydF9kZiAlPiUgCiAgbXV0YXRlKGRhdGFfdHlwZSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChkYXRhc2V0LCAiRk1CTiIpLCAiRk1CTiIsICJBU1YiKSkgJT4lCiAgbXV0YXRlKGRwbHlyOjphY3Jvc3MoLmNvbHMgPSBzYW1wbGVzOnByb3BfdGF4YSwgYXMubnVtZXJpYykpICU+JQogIG11dGF0ZShzdGFnZSA9IGFzX2ZhY3RvcihzdGFnZSkpCmlmKHZlcmJvc2Vfb3V0cHV0KSB7CiAgcHJpbnQoZmlsdGVyaW5nX3JlcG9ydF9kZikKICBwcmludChmaWx0ZXJpbmdfcmVwb3J0X2RmICU+JQogICAgICAgICAgZHBseXI6OmZpbHRlcihzdGFnZSAhPSAib3JpZ2luYWwiICYgc3RhZ2UgIT0gInBydW5lIHNhbXBsZXMiKSAlPiUKICAgICAgICAgIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBkYXRhX3R5cGUsIHkgPSBwcm9wX3RheGEpKSArCiAgICAgICAgICBmYWNldF93cmFwKH5zdGFnZSkgKwogICAgICAgICAgZ2VvbV9ib3hwbG90KCkgKwogICAgICAgICAgbGFicyh4ID0gImRhdGEgdHlwZSIsIHkgPSAicHJvcC4gdGF4YSBsZWZ0IikpCiAgc3VtbV9maWx0ZXJpbmcgPC0gZmlsdGVyaW5nX3JlcG9ydF9kZiAlPiUKICAgIGdyb3VwX2J5KGRhdGFfdHlwZSwgc3RhZ2UpICU+JQogICAgc3VtbWFyaXplKG1pbl9zZXEgPSBtaW4ocHJvcF9zZXEpLCAKICAgICAgICAgICAgICBtYXhfc2VxID0gbWF4KHByb3Bfc2VxKSwKICAgICAgICAgICAgICBtaW5fdGF4YSA9IG1pbihwcm9wX3RheGEpLAogICAgICAgICAgICAgIG1heF90YXhhID0gbWF4KHByb3BfdGF4YSkpCiAgcHJpbnQoc3VtbV9maWx0ZXJpbmcpCn0KaWYocGxheV9hdWRpbykgYmVlcChzb3VuZCA9IDYpCmlmKGtlZXBfdGltZSkgdG9jKCkKYGBgCgpUaGUgcmVkdWN0aW9uIGluIG51bWJlciBvZiAidGF4YSIgaXMgZHJhbWF0aWMgaW4gbW9zdCBjYXNlcyAoZnJvbSAwLjAxMCB0byAwLjIxIGxlZnQpLCB3aGlsZSBiZXR3ZWVuIDAuOTg0IGFuZCAwLjk5OSBvZiB0aGUgc2VxdWVuY2VzIGFyZSBsZWZ0LgoKIyBJbmZlcnJpbmcgdGhlIG5ldHdvcmtzLgoKSSB3aWxsIG5vdyB0cnkgTWljcm9iaWFsIEFzc29jaWF0aW9uIE5ldHdvcmsgaW5mZXJlbmNlLCB3aXRoIHRoZSBtZXRob2RzIGFuZCBwYXJhbWV0ZXJzIHNwZWNpZmllZCBpbiB0aGUgb3B0aW9ucyBjaHVuay4gU2VwYXJhdGUgbGlzdHMgd2lsbCBiZSBnZW5lcmF0ZWQgZm9yIGVhY2ggbWV0aG9kLiAgCkFuIGVycm9yIHRyYXBwaW5nIHJvdXRpbmUgaGFzIGJlZW4gaW1wbGVtZW50ZWQ6IGlmIE1BTiBlc3RpbWF0aW9uIGZhaWxzIGEgdHJ5LWVycm9yIG9iamVjdCByYXRoZXIgdGhhbiBhbiBvYmplY3Qgb2YgY2xhc3MgbWljcm9OZXQgd2lsbCBiZSByZXR1cm5lZC4gIApBbGwgdGhlIHJldHVybmVkIG9iamVjdHMgd2lsbCBiZSBzYXZlZCBpbiBhIGxpc3QgKDEgc2xvdCBmb3IgZWFjaCBwaHlsb3NlcSBvYmplY3QsIGVhY2ggd2l0aCAxIHNsb3QgZm9yIGVhY2ggaW5mZXJlbmNlIG1ldGhvZCkuICAKCmBgYHtyIE1BTl9pbmZlcmVuY2VfMSwgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJNaWNyb2JpYWwgYXNzb2NpYXRpb24gbmV0d29yayBpbmZlcmVuY2UiKQppZih2ZXJib3NlX291dHB1dCkgY2F0KCJQbGVhc2UgYmUgcGF0aWVudCwgdGhpcyB3aWxsIHRha2UgYSB3aGlsZS4uLlxuIikKIyBsaXN0IGZvciByZXN1bHRzLCAxIHNsb3QgZm9yIGVhY2ggb2JqZWN0Ck1BTl9pbmZfcmVzdWx0cyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgocGh5c2VxX2xpc3RfNSkpCiMgY3JlYXRlIGxpc3QgZm9yIG1ldGhvZHMsIDEgc2xvdCBmb3IgZWFjaCBtZXRob2QKaW5mX21ldGhfbGlzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgoaW5mX21ldGhvZHMpKQoKZm9yKGkgaW4gc2VxX2Fsb25nKHBoeXNlcV9saXN0XzUpKXsKICBuYW1lIDwtIG5hbWVzKHBoeXNlcV9saXN0XzUpW2ldCiAgaWYodmVyYm9zZV9vdXRwdXQpIGNhdCgiXG4iLCAiSW5mZXJyaW5nIG5ldHdvcmsocykgZm9yICIsIG5hbWUsICIsICIsIGksICJvZiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aChwaHlzZXFfbGlzdF81KSwgIlxuIiwgc2VwPSIgIikKICBpZihrZWVwX3RpbWUpIHsKICAgIHRpY21lc3NhZ2Vfb2JqZWN0IDwtIHBhc3RlKCJtaWNyb2JpYWwgYXNzb2NpYXRpb24gbmV0d29yayBpbmZlcmVuY2UsICIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lLCAiLCAiLCBpLCAiIG9mICIsIGxlbmd0aChwaHlzZXFfbGlzdF81KSwgc2VwID0gIiIpCiAgICB0aWModGljbWVzc2FnZV9vYmplY3QpCiAgfQogIGZvcihqIGluIHNlcV9hbG9uZyhpbmZfbWV0aG9kcykpewogICAgaWYoa2VlcF90aW1lKXsKICAgICAgdGljbWVzc2FnZV9tZXRob2QgPC0gcGFzdGUoIlxuIiwgImluZmVyZW5jZSB3aXRoIG1ldGhvZCAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmZfbWV0aG9kc1tqXSwgIiwgIiwgaiwgIiBvZiAiLCBsZW5ndGgoaW5mX21ldGhvZHMpLCBzZXAgPSAiIikKICAgICAgdGljKHRpY21lc3NhZ2VfbWV0aG9kKQogICAgfQogICAgaW5mbWV0aG9kIDwtIGluZl9tZXRob2RzW1tqXV0KICAgIGluZnBhcmFtIDwtIGluZl9tZXRob2RzX3BhcmFtW1tqXV0KICAgCiAgICAjIGluZnJlbmNlIGlzIGNhcnJpZWQgb3V0IGhlcmUgdXNpbmcgYSB1c2VyIGRlZmluZWQgZnVuY3Rpb24gbG9hZGVkIHdpdGggdGhlIGBzb3VyY2UoKWAgY29tbWFuZCAKICAgICMgeW91IGRvIG5vdCBuZWVkIHRvIHByb3ZpZGUgbXVjaCBkZXRhaWwsIGV2ZXJ5dGhpbmcgaXMgdGFrZW4gY2FyZSBvZiBpbiB0aGUgb3B0aW9ucwogICAgIyBvZiBjb3Vyc2UgY291bGQgYmUgY3VzdG9taXplZCBpbiB0aGUgZnVuY3Rpb24gY2FsbAogICAgaW5mX21ldGhfbGlzdFtbal1dIDwtIGluZmVyX01BTihteXBoeXNlcSA9IHBoeXNlcV9saXN0XzVbW2ldXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5mX21ldGhvZCA9IGluZm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kX3BhcmFtZXRlcnMgPSBpbmZwYXJhbSkKICAgIG5hbWVzKGluZl9tZXRoX2xpc3QpW2pdIDwtIGluZm1ldGhvZAogICAgaWYoa2VlcF90aW1lKSB0b2MoKQogIH0KICBNQU5faW5mX3Jlc3VsdHNbW2ldXSA8LSBpbmZfbWV0aF9saXN0CiAgbmFtZXMoTUFOX2luZl9yZXN1bHRzKVtpXSA8LSBuYW1lCiAgaWYoa2VlcF90aW1lKSB0b2MoKQp9CiMgY3JlYXRlIGEgcmVwb3J0CmluZmVyZW5jZV9yZXBvcnQgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKE1BTl9pbmZfcmVzdWx0cykpCmZvcihpIGluIHNlcV9hbG9uZyhNQU5faW5mX3Jlc3VsdHMpKXsKICBpbmZlcmVuY2VfcmVwb3J0W1tpXV08LW1hcF9kZnIoTUFOX2luZl9yZXN1bHRzW1tpXV0sIGNsYXNzLCAuaWQgPSAibWV0aG9kIikKICBuYW1lcyhpbmZlcmVuY2VfcmVwb3J0KVtpXTwtbmFtZXMoTUFOX2luZl9yZXN1bHRzKVtpXQp9CmluZmVyZW5jZV9yZXBvcnRfZGYgPC0gYmluZF9yb3dzKGluZmVyZW5jZV9yZXBvcnQsIC5pZCA9ICJkYXRhc2V0IikKCiMgc2F2ZSB0aGUgbGlzdCBhbmQgZG8gc29tZSBjbGVhbi11cApzYXZlKE1BTl9pbmZfcmVzdWx0cywgZmlsZSA9IGZpbGUucGF0aChvdXRwdXRfZm9sZGVyLCBwYXN0ZShvdXRfZmlsZW5hbWVfcHJlZiwiX01BTmxpc3QuUmRhdGEiKSkpCnJtKGluZl9tZXRoX2xpc3QsIGluZmVyZW5jZV9yZXBvcnQpCmlmKHBsYXlfYXVkaW8pIGJlZXAoc291bmQgPSA2KQppZihrZWVwX3RpbWUpIHRvYygpCmBgYAoKIyBBbmFseXplIHRoZSBuZXR3b3Jrcy4gIAoKVGhlIGluZmVycmVkIG5ldHdvcmtzIHdpbGwgYmUgYW5hbHl6ZWQgdXNpbmcgTmV0Q29NaTo6bmV0QW5hbHl6ZSgpIGFuZCB0aGUgcmVzdWx0aW5nIG9iamVjdHMgd2lsbCBiZSBwcm9jZXNzZWQgZnVydGhlci4gIApOZXR3b3JrLCBub2RlIGFuZCBlZGdlIHN0YXRzIHdpbGwgYmUgZXh0cmFjdGVkIHRvIGRhdGEgZnJhbWVzL3RpYmJsZXMgZm9yIGZ1cnRoZXIgcHJvY2Vzc2luZy4gIApBIGZldyBleHRyYSBhbmFseXNlcyB3aWxsIGJlIGNhcnJpZWQgb3V0IHVzaW5nIHBhY2thZ2UgcGh5bG9zZXEgdG8gYWRkIGRpdmVyc2l0eSBhbmQgZXZlbm5lc3MgaW5kaWNlcyB0byB0aGUgbWV0YWRhdGEuICAKCmBgYHtyIGFuYWx5emVfbmV0d29ya3MsIGRwaSA9IDk2fQoKaWYoa2VlcF90aW1lKSB0aWMoImNhbGN1bGF0ZSBkaXZlcnNpdHkgcG9zdC1maWx0ZXIiKQoKIyBlc3RpbWF0ZSBkaXZlcnNpdHkgZm9yIGVhY2ggb2JqZWN0IG9mIHRoZSBwaHlzZXFfbGlzdF81LCByZXR1cm5zIGEgbGlzdCB3aXRoIHRoZSByZXN1bHRzCiMgZXh0cmFjdCBhbmQgcHV0IHRvZ2V0aGVyIHdpdGggbWV0YWRhdGEKIyB3aWxsIGdlbmVyYXRlIHdhcm5pbmdzCgpkaXZfZXN0X3Bvc3RmaWx0ZXIgPC0gbWFwX2RmcihwaHlzZXFfbGlzdF81LCBlc3RpbWF0ZV9yaWNobmVzcywgc3BsaXQgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZXMgPSBjKCJPYnNlcnZlZCIsIkNoYW8xIiwiU2hhbm5vbiIpLCAuaWQgPSAibGFiZWwiKQpkaXZfZXN0X3Bvc3RmaWx0ZXIgPC0gbXV0YXRlKGRpdl9lc3RfcG9zdGZpbHRlciwgUGllbG91X0ogPSBTaGFubm9uL2xvZyhPYnNlcnZlZCkpCgojIGNhbGN1bGF0ZSBhbmQgYWRkIGF2ZXJhZ2UgQnJheS1DdXJ0aXMgZGlzc2ltaWxhcml0eQptZWFuYmNkaXN0IDwtIG1hcChwaHlzZXFfbGlzdF81LCBwaHlsb3NlcTo6ZGlzdGFuY2UsIG1ldGhvZD0iYnJheSIpCmRpdl9lc3RfcG9zdGZpbHRlciRhdmVfQkMgPC0gdW5saXN0KG1hcChtZWFuYmNkaXN0LCBtZWFuKSkKCnJvdy5uYW1lcyhkaXZfZXN0X3Bvc3RmaWx0ZXIpIDwtIG5hbWVzKHBoeXNlcV9saXN0XzUpCiMgc2F2ZSBmb3IgZnVydGhlciB1c2UKc2F2ZShkaXZfZXN0X3Bvc3RmaWx0ZXIsIAogICAgIGZpbGUgPSBwYXN0ZShmaWxlLnBhdGgob3V0cHV0X2ZvbGRlcixvdXRfZmlsZW5hbWVfcHJlZiksICJfZGl2cG9zdGZpbHRlci5SZGF0YSIsc2VwPSIiKSkKCiMgYW4gYWx0ZXJuYXRpdmUgY291bGQgYmUgdG8gdXNlIGl0IG9uIGRpdl9lc3RfcHJlZmlsdGVyCnN0dWR5X21ldGFkYXRhIDwtIGxlZnRfam9pbihzdHVkeV9tZXRhZGF0YSwgZGl2X2VzdF9wb3N0ZmlsdGVyKQoKaWYoa2VlcF90aW1lKSB0b2MoKQoKIyBjYWxjdWxhdGUgbmV0d29yayBzdGF0aXN0aWNzIHdpdGggbmV0QW5hbHl6ZQppZihrZWVwX3RpbWUpIHRpYygiQ2FsY3VsYXRlIG5ldHdvcmsgc3RhdGlzdGljcyIpCgojIHRoZSBsaXN0IGZvciB0aGUgbWV0aG9kcyB3aXRoaW4gdGhlIGRhdGFzZXQKbmV0X3N0YXRzIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IGxlbmd0aChNQU5faW5mX3Jlc3VsdHMpKQojIG5ldF9zdGF0X3Jlc3VsdHMgaXMgYSBsaXN0LCBkbyB0aGUgY2FsY3VsYXRpb24gZm9yIGVhY2ggb2YgdGhlIGRhdGFzZXRzLCBhbGwgaW5mZXJlbmNlIG1ldGhvZHMKZm9yKGkgaW4gc2VxX2Fsb25nKE1BTl9pbmZfcmVzdWx0cykpewogIG5hbWUgPC0gbmFtZXMoTUFOX2luZl9yZXN1bHRzKVtpXQogIGlmKHZlcmJvc2Vfb3V0cHV0KSBjYXQoIkNhbGN1bGF0aW5nIG5ldHdvcmsgc3RhdHMgZm9yIixuYW1lLCJcbiIpCiAgbmV0X3N0YXRfcmVzdWx0cyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgoTUFOX2luZl9yZXN1bHRzW1tpXV0pKQogIGZvcihqIGluIHNlcV9hbG9uZyhNQU5faW5mX3Jlc3VsdHNbW2ldXSkpewogICAgbXRoZCA8LSBuYW1lcyhNQU5faW5mX3Jlc3VsdHNbW2ldXSlbal0KICAgIGlmKHZlcmJvc2Vfb3V0cHV0KSBjYXQoIm1ldGhvZCIsbXRoZCwiXG4iKQogICAgIyBjb25zaWRlciByZWR1Y2luZyBhcmd1bWVudCBodWJRdWFudCB0byAwLjc1LTAuOTAgZGVmYXVsdCBpcyAwLjk1KSwKICAgIG5ldF9zdGF0X3Jlc3VsdHNbW2pdXSA8LSBjYWxjdWxhdGVfbmV0X3N0YXRzKE1BTl9pbmZfcmVzdWx0c1tbaV1dW1tqXV0pCiAgfQogIG5hbWVzKG5ldF9zdGF0X3Jlc3VsdHMpPC1uYW1lcyhNQU5faW5mX3Jlc3VsdHNbW2ldXSkKICBuZXRfc3RhdHNbW2ldXSA8LSBuZXRfc3RhdF9yZXN1bHRzCiAgbmFtZXMobmV0X3N0YXRzKVtpXTwtbmFtZXMoTUFOX2luZl9yZXN1bHRzKVtpXQp9CnJtKG5ldF9zdGF0X3Jlc3VsdHMsIG1lYW5iY2Rpc3QsIG10aGQpCmlmKGtlZXBfdGltZSkgdG9jKCkKCiMgZXh0cmFjdGluZyBnbG9iYWwgbmV0d29yayBwcm9wZXJ0aWVzCgppZihrZWVwX3RpbWUpIHRpYygiRXh0cmFjdGlvbiBnbG9iYWwgbmV0d29yayBwcm9wZXJ0aWVzIikKIyBleHRyYWN0aW5nIHRoZSBnbG9iYWwgbmV0d29yayBzdGF0cyAKZ2xvYmFsX3Byb3BzX2xpc3RfYSA8LXZlY3RvcigibGlzdCIsbGVuZ3RoKG5ldF9zdGF0cykpCmdsb2JhbF9wcm9wc19saXN0X2wgPC12ZWN0b3IoImxpc3QiLGxlbmd0aChuZXRfc3RhdHMpKSAKZ2xvYmFsX3Byb3BzX2xpc3RfYWxsIDwtdmVjdG9yKCJsaXN0IixsZW5ndGgoaW5mX21ldGhvZHMpKQpnbG9iYWxfcHJvcHNfbGlzdF9sY2MgPC12ZWN0b3IoImxpc3QiLGxlbmd0aChpbmZfbWV0aG9kcykpCmZvciAoaSBpbiBzZXFfYWxvbmcobmV0X3N0YXRzKSl7CiAgZGF0YXNldCA8LSBuYW1lcyhuZXRfc3RhdHMpW2ldCiAgZm9yIChqIGluIHNlcV9hbG9uZyhuZXRfc3RhdHNbW2ldXSkpewogICAgaWYoY2xhc3MobmV0X3N0YXRzW1tpXV1bW2pdXSkhPSAibWljcm9OZXRQcm9wcyIpewogICAgICBuZXh0CiAgICB9IGVsc2V7CiAgICAgIG5ub2RlcyA8LSBzdW0obmV0X3N0YXRzW1tpXV1bW2pdXSRjZW50cmFsaXRpZXMkZGVncmVlMT4wKQogICAgICBudGF4YSA8LSBucm93KG5ldF9zdGF0c1tbaV1dW1tqXV0kaW5wdXQkYXNzb01hdDEpCiAgICAgIG5wb3NlZGdlIDwtIHN1bShuZXRfc3RhdHNbW2ldXVtbal1dJGlucHV0JGFzc29NYXQxW2xvd2VyLnRyaShuZXRfc3RhdHNbW2ldXVtbal1dJGlucHV0JGFzc29NYXQxKV0+MCkKICAgICAgbm5lZ2VkZ2UgPC0gc3VtKG5ldF9zdGF0c1tbaV1dW1tqXV0kaW5wdXQkYXNzb01hdDFbbG93ZXIudHJpKG5ldF9zdGF0c1tbaV1dW1tqXV0kaW5wdXQkYXNzb01hdDEpXTwwKQogICAgICBleHRyYV9wcm9wX3ZlY3RvciA8LSBjKG5ub2RlcywgbnRheGEsIG5wb3NlZGdlLCBubmVnZWRnZSkKICAgICAgbmFtZXMoZXh0cmFfcHJvcF92ZWN0b3IpIDwtIGMoIm5ub2RlcyIsICJudGF4YSIsICJucG9zZWRnZSIsICJubmVnZWRnZSIpCiAgICAgIGdsb2JhbF9wcm9wc19saXN0X2FsbFtbal1dIDwtIGModW5saXN0KG5ldF9zdGF0c1tbaV1dW1tqXV0kZ2xvYmFsUHJvcHMpLGV4dHJhX3Byb3BfdmVjdG9yKQogICAgICBnbG9iYWxfcHJvcHNfbGlzdF9sY2NbW2pdXSA8LSBjKHVubGlzdChuZXRfc3RhdHNbW2ldXVtbal1dJGdsb2JhbFByb3BzTENDKSxleHRyYV9wcm9wX3ZlY3RvcikKICAgICAgbmFtZXMoZ2xvYmFsX3Byb3BzX2xpc3RfYWxsKVtqXSA8LSBuYW1lcyhnbG9iYWxfcHJvcHNfbGlzdF9sY2MpW2pdPC0gbmFtZXMobmV0X3N0YXRzW1tpXV1bal0pCiAgICB9CiAgICAjIGNyZWF0ZSBkYXRhIGZyYW1lIHdpdGggcmVzdWx0cwogICAgYWxsX2RmIDwtIGJpbmRfcm93cyhnbG9iYWxfcHJvcHNfbGlzdF9hbGwsIC5pZCA9ICJtZXRob2QiKQogICAgbGNjX2RmIDwtIGJpbmRfcm93cyhnbG9iYWxfcHJvcHNfbGlzdF9sY2MsIC5pZCA9ICJtZXRob2QiKSAKICB9CiAgZ2xvYmFsX3Byb3BzX2xpc3RfYVtbaV1dIDwtIGFsbF9kZgogIGdsb2JhbF9wcm9wc19saXN0X2xbW2ldXSA8LSBsY2NfZGYKICBuYW1lcyhnbG9iYWxfcHJvcHNfbGlzdF9hKVtpXSA8LSBuYW1lcyhnbG9iYWxfcHJvcHNfbGlzdF9sKVtpXSA8LSBuYW1lcyhuZXRfc3RhdHMpW2ldCn0KIyBub3RlIHRoYXQgc3RyX3N1YiBvbmx5IHdvcmtzIGlmIHlvdSBoYXZlIDw9OSBpbmZlcmVuY2UgbWV0aG9kcyBpbiBpbmZfbWV0aG9kcwpnbG9iYWxfYWxsX2RmIDwtIGJpbmRfcm93cyhnbG9iYWxfcHJvcHNfbGlzdF9hLCAuaWQgPSAiZGF0YXNldCIpIApnbG9iYWxfbGNjX2RmIDwtIGJpbmRfcm93cyhnbG9iYWxfcHJvcHNfbGlzdF9sLCAuaWQgPSAiZGF0YXNldCIpIAoKCmdsb2JhbF9hbGxfZGYgPC0gbGVmdF9qb2luKGdsb2JhbF9hbGxfZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3Qoc3R1ZHlfbWV0YWRhdGEsbGFiZWwsIG9ial90eXBlLCB0YXJnZXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVnaW9uLCBwbGF0Zm9ybSwgc2FtcGxlcywgdHlwZSwgT2JzZXJ2ZWQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2hhbzEsIFNoYW5ub24sIFBpZWxvdV9KLCBhdmVfQkMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJkYXRhc2V0IiA9ICJsYWJlbCIpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+bmFfaWYoLixJbmYpKSkKZ2xvYmFsX2xjY19kZiA8LSBsZWZ0X2pvaW4oZ2xvYmFsX2xjY19kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChzdHVkeV9tZXRhZGF0YSxsYWJlbCwgb2JqX3R5cGUsIHRhcmdldCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb24sIHBsYXRmb3JtLCBzYW1wbGVzLCB0eXBlLCBPYnNlcnZlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDaGFvMSwgU2hhbm5vbiwgUGllbG91X0osIGF2ZV9CQyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImRhdGFzZXQiID0gImxhYmVsIikpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH5uYV9pZiguLEluZikpKQoKIyBib3RoIG1pZ2h0IGNvbnRhaW4gSW5mIHdoaWNoIGFyZSByZXBsYWNlZCBieSBOQSAodXNpbmcgZHBseXI6Om5hX2lmKCkpIHRvIGJlIGhhbmRsZWQKIyBjb3JyZWN0bHkgaWYgZG9pbmcgUENBIGJ5IHBhaXJ3aXNlIGRlbGV0aW9uLgojIHRoZSBwcm9ibGVtIG9ubHkgb2NjdXJzIGluIGF2UGF0aDEgYW5kIGNsdXN0Q29lZjEsIGJ1dCBJIGFtIGhhbmRsaW5nIGl0IHdpdGggYSBzY29wZWQgbXV0YXRlCiMgYSBiZXR0ZXIgc29sdXRpb24gbWlnaHQgYmUgdGhlIHVzZSBvZiBoYWJsYXI6OnJhdGlvbmFsaXplKCkKCgojIHNhdmUgdGhlIGRhdGEgZnJhbWVzIGZvciBmdXJ0aGVyIHVzZQp3cml0ZV90c3YoZ2xvYmFsX2FsbF9kZiwgZmlsZSA9IHBhc3RlKGZpbGUucGF0aChvdXRwdXRfZm9sZGVyLG91dF9maWxlbmFtZV9wcmVmKSwgIl9uZXRwcm9wYWxsLnR4dCIsc2VwPSIiKSkKd3JpdGVfdHN2KGdsb2JhbF9sY2NfZGYsIGZpbGUgPSBwYXN0ZShmaWxlLnBhdGgob3V0cHV0X2ZvbGRlcixvdXRfZmlsZW5hbWVfcHJlZiksICJfbmV0cHJvcGxjYy50eHQiLHNlcD0iIikpCgojIHByaW50IGEgc3VtbWFyeSB0YWJsZQpnbG9iYWxfYWxsX2RmCgpybShnbG9iYWxfcHJvcHNfbGlzdF9hLCBnbG9iYWxfcHJvcHNfbGlzdF9sLCBnbG9iYWxfcHJvcHNfbGlzdF9hbGwsIAogICBnbG9iYWxfcHJvcHNfbGlzdF9sY2MsIGFsbF9kZiwgbGNjX2RmLCBubm9kZXMsIG50YXhhLCBucG9zZWRnZSwgbm5lZ2VkZ2UsIGV4dHJhX3Byb3BfdmVjdG9yKQppZihwbGF5X2F1ZGlvKSBiZWVwKHNvdW5kID0gNikKaWYoa2VlcF90aW1lKSB0b2MoKQpgYGAKCkdsb2JhbCBuZXR3b3JrIHByb3BlcnRpZXMgaGF2ZSBiZWVuIHNhdmVkIGFzIGRhdGEgZnJhbWVzIGZvciBmdXJ0aGVyIHVzZS4gVGhleSB3aWxsIGJlIHVzZWQgdG9nZXRoZXIgdGhvc2UgZ2VuZXJhdGVkIGZvciB0aGUgb3RoZXIgZGF0YSBzZXRzLgoKIyMgTm9kZSBwcm9wZXJ0aWVzLgoKTm9kZSBwcm9wZXJ0aWVzIGNhbiBiZSBleHRyYWN0ZWQgZnJvbSB0aGUgbWljcm9OZXRQcm9wcyBvYmplY3RzIGFuZCBzYXZlZCBmb3IgZnVydGhlciB1c2UuICAKCmBgYHtyIGV4dHJhY3Rfbm9kZXMsIGRwaSA9IDk2fQoKIyBleHRyYWN0IG5vZGUgcHJvcGVydGllcwojIHVzaW5nIGEgbG9vcCB0YWtlcyBzbGlnaHRseSBsb25nZXIgdGhhdCB1c2luZyBmdW5jdGlvbmFscyBidXQgaGFuZGxlcyBuYW1lcyBiZXR0ZXIKIyBub3RlIHRoYXQgd2hlbiB1c2luZyBBU1ZzIGNvbXBhcmluZyBub2RlcyBiZXR3ZWVuIGRhdGFzZXRzIGRvZXMgbm90IG1ha2UgbXVjaCBzZW5zZQoKaWYoa2VlcF90aW1lKSB0aWMoIkV4dHJhY3Rpbmcgbm9kZSBwcm9wZXJ0aWVzIikKCm5vZGVfc3RhdHMgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKG5ldF9zdGF0cykpCmZvciAoaSBpbiBzZXFfYWxvbmcobmV0X3N0YXRzKSkgewogIG5vZGVfcHJvcGVydGllcyA8LSBub2RlX3N0YXRfbGlzdFtbaV1dCiAgCiAgaWYodmVyYm9zZV9vdXRwdXQpIGNhdCgiZXh0cmFjdGluZyBub2RlIHN0YXRzIGZvciIsIG5hbWVzKG5ldF9zdGF0cylbaV0sIlxuIikKICBmb3IgKGogaW4gc2VxX2Fsb25nKG5ldF9zdGF0c1tbaV1dKSkgewogICAgaWYgKGNsYXNzKG5ldF9zdGF0c1tbaV1dW1tqXV0pID09ICJtaWNyb05ldFByb3BzIikgewogICAgICBub2RlX3N0YXRzW1tpXV1bW2pdXSA8LSBleHRyYWN0X25vZGVfc3RhdHMobmV0X3N0YXRfbGlzdCA9IG5ldF9zdGF0c1tbaV1dW1tqXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZXN0YXQgPSBub2RlX3Byb3BlcnRpZXMpCiAgICAgIG1ldGhvZCA8LSBuYW1lcyhuZXRfc3RhdHNbW2ldXSlbal0KICAgICAgZGF0YXNldCA8LSBuYW1lcyhuZXRfc3RhdHMpW2ldCiAgICAgIG5yb3dzIDwtIG5yb3cobm9kZV9zdGF0c1tbaV1dW1tqXV0pCiAgICAgIG5vZGVfc3RhdHNbW2ldXVtbal1dIDwtIGJpbmRfY29scygKICAgICAgICBkYXRhc2V0ID0gcmVwKGRhdGFzZXQsbnJvd3MpLAogICAgICAgIG1ldGhvZCA9IHJlcChtZXRob2QsbnJvd3MpLAogICAgICAgIG5vZGVfc3RhdHNbW2ldXVtbal1dCiAgICAgICAgKQogICAgfSBlbHNlIHsKICAgICAgY2F0KCJubyBub2RlIHN0YXRzIHRvIHJldHVybiBmb3IiLAogICAgICAgICAgbmFtZXMobmV0X3N0YXRzKVtpXSwKICAgICAgICAgIG5hbWVzKG5vZGVfc3RhdHNbW2ldXSlbal0sCiAgICAgICAgICAiXG4iKQogICAgICBuZXh0CiAgICB9CiAgfQogIG5vZGVfc3RhdHNbW2ldXTwtYmluZF9yb3dzKG5vZGVfc3RhdHNbW2ldXSkKfQpub2RlX3N0YXRzX2RmIDwtIGJpbmRfcm93cyhub2RlX3N0YXRzKQojIHBlcmZvcm0gc29tZSB0aWR5aW5nCm5vZGVfc3RhdHNfZGYgPC0gbm9kZV9zdGF0c19kZiAlPiUKICB0aWR5cjo6c2VwYXJhdGUoZGF0YXNldCwgaW50byA9IGMoIlN0dWR5IiwgIkFjY25fbiIsICJzdWYiKSwgc2VwID0gIl8iLCByZW1vdmUgPSBGKSAlPiUgCiAgZHBseXI6OnNlbGVjdCgtQWNjbl9uLCAtc3VmKSAlPiUKICBtdXRhdGUobGFiZWwyID0gaWZfZWxzZSghc3RyX2RldGVjdChkYXRhc2V0LCAiRk1CTiIpLHN0cl9jKGxhYmVsLCBTdHVkeSwgc2VwID0gIl8iKSwgbGFiZWwpKQoKIyBsYWJlbDIgaXMgb25seSBuZWNlc3Nhcnkgd2hlbiB1c2luZyBBU1ZzIG9yIE9UVXMsIG5vdCBpZiB0aGVyZSBoYXMgYmVlbiB0YXhvbm9taWMgYWdnbG9tZXJhdGlvbgojIGNvbnNpZGVyIHJlbW92aW5nIHRoZSBtdXRhdGUgaW5zdHJ1Y3Rpb24KCndyaXRlX3Rzdihub2RlX3N0YXRzX2RmLCBmaWxlID0gcGFzdGUoZmlsZS5wYXRoKG91dHB1dF9mb2xkZXIsb3V0X2ZpbGVuYW1lX3ByZWYpLCAiX25vZGVzdGF0c19kZi50eHQiLHNlcD0iIikpCnJtKG5vZGVfc3RhdHMpCmlmKHBsYXlfYXVkaW8pIGJlZXAoc291bmQgPSA2KQppZihrZWVwX3RpbWUpIHRvYygpCgpgYGAKCiMjIEVkZ2UgcHJvcGVydGllcy4gIAoKRWRnZXMgYW5kIGVkZ2UgcHJvcGVydGllcyBjYW4gYWxzbyBiZSBleHRyYWN0ZWQgZm9yIGZ1cnRoZXIgdXNlLiBJbXBvcnRhbnQgZWRnZSBwcm9wZXJ0aWVzIGFyZToKCiogZWRnZSB0eXBlIChwb3NpdGl2ZSBvciBuZWdhdGl2ZSkgIAoKKiBlZGdlIHdlaWdodCBhbmQgYXNzb2NpYXRpb24gbWVhc3VyZQoKKiBlZGdlIGJldHdlZW5uZXNzICAKCkl0IGlzIGFsc28gY29udmVuaWVudCB0byBjb21wYXJlIGVkZ2VzIGFtb25nIGRpZmZlcmVudCBncmFwaHMgKGFuZCB0byBkbyBzbyBpdCBtaWdodCBiZSBjb252ZW5pZW50IHRvIG1ha2Ugc3VyZSB0aGF0ICJ0byIgYW5kICJmcm9tIiBhcmUgYWx3YXlzIGluIGFscGhhYmV0aWNhbCBvcmRlciwgd2hpY2ggaXMgYWx3YXlzIHRydWUgd2l0aGluIHRoZSBzYW1lIGdyYXBoIGJ1dCBtaWdodCBub3QgYmUgbmVjZXNzYXJpbHkgdHJ1ZSBhbW9uZyBkaWZmZXJlbnQgZ3JhcGhzKS4gIApUaGUgZm9sbG93aW5nIGNodW5rIHdpbGwgKG9wdGlvbmFsbHkpOiAKCiogaW50ZWdyYXRlIG5vZGUgKGNhbGN1bGF0ZWQgd2l0aCBgbmV0QW5hbHlzZSgpYCkgaW4gdGhlIHRpZHlncmFwaCBsaXN0LCAKCiogY2FsY3VsYXRlIGVkZ2UgYmV0d2Vlbm5lc3MsICAKCiogY29tcGFyZSBuZXR3b3JrcyAod2l0aGluIGRhdGFzZXQpIGJ5IHByb2R1Y2luZyBhbmQgc2F2aW5nIFZlbm4gZGlhZ3JhbXMgb2YgdGhlIGVkZ2VzCgpGaW5hbGx5LCBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgZWRnZXMgd2lsbCBiZSBwcm9kdWNlZCBmb3IgZnVydGhlciB1c2UuICAKCgpgYGB7ciBmdXJ0aGVyX2VkZ2Vfbm9kZV9wcm9wZXJ0aWVzLCBkcGkgPSA5Nn0KCmlmKGtlZXBfdGltZSkgdGljKCJMaXN0IHdpdGggdGlkeWdyYXBoIG9iamVjdHMgY3JlYXRlZCIpCgojIGNyZWF0aW5nIGEgdGlkeWdyYXBoIG9iamVjdCBmb3IgZWFjaCBuZXQKdGlkeWdyYXBoX2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKE1BTl9pbmZfcmVzdWx0cykpCgpmb3IoaSBpbiBzZXFfYWxvbmcoTUFOX2luZl9yZXN1bHRzKSl7CiAgaWYodmVyYm9zZV9vdXRwdXQpIGNhdCgiQ29udmVydGluZyBpbiB0aWR5Z3JhcGhzIGZvciIsIG5hbWVzKE1BTl9pbmZfcmVzdWx0cylbaV0sIlxuIikKICAjIHRoZSB3YXJuaW5nLCBpZiBhbnksIGlzIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLCBzaG91bGQgY29uc2lkZXIgcGFzc2luZyBuYW1lcyBvZiBkYXRhc2V0cyBhbmQgbWV0aG9kcwogIHRpZHlncmFwaF9saXN0W1tpXV0gPC0gTUFOX2luZl9yZXN1bHRzW1tpXV0gJT4lIG1hcChtaWNyb05ldF90b190aWR5Z3JhcGgsIGZhaWxfd19lcnIgPSBGLCB1c2VfYXNzb19tYXRyaXggPSBUKQp9Cm5hbWVzKHRpZHlncmFwaF9saXN0KTwtbmFtZXMoTUFOX2luZl9yZXN1bHRzKQppZihrZWVwX3RpbWUpIHRvYygpCgojIG9wdGlvbmFsbHkgbWVyZ2UgZnVydGhlciBub2RlIHN0YXRzIChkZXBlbmRzIG9uIG1lcmdlX25fc3RhdHMpIAojIGFuZCBjYWxjdWxhdGUgZWRnZSBiZXR3ZWVubmVzcyAoZGVwZW5kcyBvbiBjYWxjX2VfYmV0dykKIyBJIGFtIHVzaW5nIGEgbG9vcAppZihrZWVwX3RpbWUpIHRpYygiU3RhdHMgYWRkZWQgdG8gdGlkeWdyYXBocywgZWRnZSBkYXRhZnJhbWUgY3JlYXRlZCIpCnRpZHlncmFwaF9saXN0X3dzdGF0cyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgodGlkeWdyYXBoX2xpc3QpKQojIHRoZSBsaXN0IHdpdGggdGhlIGVkZ2UgZGF0YSBmcmFtZXMKZWRnZV9saXN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IGxlbmd0aCh0aWR5Z3JhcGhfbGlzdCkpCgpmb3IoaSBpbiBzZXFfYWxvbmcodGlkeWdyYXBoX2xpc3Rfd3N0YXRzKSl7CiAgIyBuZWVkIHRvIGJlIHJlc2V0CiAgaW5uZXJfdGdsIDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IGxlbmd0aCh0aWR5Z3JhcGhfbGlzdFtbaV1dKSkKICBpbm5lcl9lbCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgodGlkeWdyYXBoX2xpc3RbW2ldXSkpCiAgZHRzdCA8LSBuYW1lcyh0aWR5Z3JhcGhfbGlzdClbaV0KICBmb3IoaiBpbiBzZXFfYWxvbmcoaW5uZXJfdGdsKSl7CiAgICBpZihpcy50YmxfZ3JhcGgodGlkeWdyYXBoX2xpc3RbW2ldXVtbal1dKSl7CiAgICBtdGhkID0gbmFtZXModGlkeWdyYXBoX2xpc3RbW2ldXSlbal0KICAgIG5zdGF0cyA8LSBub2RlX3N0YXRzX2RmICU+JSBkcGx5cjo6ZmlsdGVyKGRhdGFzZXQgPT0gZHRzdCAmIG1ldGhvZCA9PSBtdGhkKQogICAgaW5uZXJfdGdsW1tqXV0gPC0gbWVyZ2Vfc3RhdHModGcgPSB0aWR5Z3JhcGhfbGlzdFtbaV1dW1tqXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9kZV9zdGF0cyA9IG5zdGF0cywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWJldHcgPSBjYWxjX2VfYmV0dykKICAgICAjIGV4dHJhY3QgZWRnZSB0aWJibGUKICAgIGlubmVyX2VsW1tqXV0gPC0gaW5uZXJfdGdsW1tqXV0gJT4lIAogICAgICBhY3RpdmF0ZShlZGdlcykgJT4lIAogICAgICBhc190aWJibGUoKSAlPiUKICAgICAgbXV0YXRlKG1ldGhvZCA9IG10aGQpCiAgICAjIGRvIG5hbWluZwogICAgbmFtZXMoaW5uZXJfdGdsKVtqXSA8LSBuYW1lcyhpbm5lcl9lbClbal0gPC0gbmFtZXModGlkeWdyYXBoX2xpc3RbW2ldXSlbal0KICAgIH0gZWxzZSB7CiAgICAgIG5leHQKICAgIH0KICB9CiAgdGlkeWdyYXBoX2xpc3Rfd3N0YXRzW1tpXV0gPC0gaW5uZXJfdGdsCiAgZWRnZV9saXN0W1tpXV0gPC0gYmluZF9yb3dzKGlubmVyX2VsKQogIGVkZ2VfbGlzdFtbaV1dIDwtIGVkZ2VfbGlzdFtbaV1dICU+JSAKICAgIG11dGF0ZShkYXRhc2V0ID0gZHRzdCkgJT4lIGRwbHlyOjpzZWxlY3QoZGF0YXNldCwgbWV0aG9kLCAhKGRhdGFzZXQ6bWV0aG9kKSkKICBuYW1lcyh0aWR5Z3JhcGhfbGlzdF93c3RhdHMpW2ldIDwtIG5hbWVzKGVkZ2VfbGlzdClbaV0gPC0gbmFtZXModGlkeWdyYXBoX2xpc3QpW2ldCn0Kcm0obnN0YXRzKQojIGJ1aWxkIGFuZCBmaXggdGhlIGVkZ2UgZGYKZWRnZV9saXN0X2RmIDwtIGJpbmRfcm93cyhlZGdlX2xpc3QpCgplZGdlX2xpc3RfZGYgPC0gZWRnZV9saXN0X2RmICU+JSAKICBtdXRhdGUobmFtZV9mcm9tX3NvcnRlZCA9IGlmX2Vsc2UoZnJvbV9uYW1lIDwgdG9fbmFtZSwgZnJvbV9uYW1lLCB0b19uYW1lKSwKICAgICAgICAgbmFtZV90b19zb3J0ZWQgPSBpZl9lbHNlKGZyb21fbmFtZSA8IHRvX25hbWUsIHRvX25hbWUsIGZyb21fbmFtZSkpICU+JQogIG11dGF0ZShlZGdlX25hbWUgPSBzdHJfYyhuYW1lX2Zyb21fc29ydGVkLCBuYW1lX3RvX3NvcnRlZCwgc2VwID0gIi0tIikpCndyaXRlX3RzdihlZGdlX2xpc3RfZGYsIGZpbGUgPSBwYXN0ZShmaWxlLnBhdGgob3V0cHV0X2ZvbGRlcixvdXRfZmlsZW5hbWVfcHJlZiksICJfZWRnZWxpc3RfZGYudHh0IixzZXA9IiIpKQppZihrZWVwX3RpbWUpIHRvYygpCgojIG1ha2UgVmVubiBwbG90cwoKIyBJIGFtIHVzaW5nIGEgbG9vcAppZihkb19WZW5uKXsKICBpZihrZWVwX3RpbWUpIHRpYygiVmVubiBwbG90cyBjcmVhdGVkIGFuZCBzYXZlZCIpCiAgVmVubl9saXN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCh0aWR5Z3JhcGhfbGlzdCkpCiAgZm9yKGkgaW4gc2VxX2Fsb25nKHRpZHlncmFwaF9saXN0KSl7CiAgICAjIG5lZWQgdG8gc2VsZWN0IG9ubHkgZWxlbWVudHMgb2YgY2xhc3MgdGJsX2dyYXBoCiAgICB0YmxncnBocyA8LSBtYXBfbGdsKHRpZHlncmFwaF9saXN0W1tpXV0sIGlzLnRibF9ncmFwaCkKICAgIG5hbWVzKFZlbm5fbGlzdCkgPC0gbmFtZXModGlkeWdyYXBoX2xpc3QpCiAgICBpZihsZW5ndGgodGlkeWdyYXBoX2xpc3RbW2ldXVt0YmxncnBoc10pPjEpewogICAgICBpbm5lcl9saXN0IDwtIG1hcCh0aWR5Z3JhcGhfbGlzdFtbaV1dW3RibGdycGhzXSwgZnVuY3Rpb24oeCkgYXNfaWRzKEUoeCkpKQogICAgICBWZW5uX3RpdGxlIDwtIG5hbWVzKHRpZHlncmFwaF9saXN0KVtpXQogICAgICBWZW5uX2ZpbGUgPC0gcGFzdGUoZmlsZS5wYXRoKG91dHB1dF9mb2xkZXIsb3V0X2ZpbGVuYW1lX3ByZWYpLCJfIixWZW5uX3RpdGxlLCAiX3Zlbm5zLnRpZmYiLHNlcD0iIikKICAgICAgbXlfZmlsbCA8LSAoMjo1KVsxOmxlbmd0aCh0aWR5Z3JhcGhfbGlzdFtbaV1dW3RibGdycGhzXSldCiAgICAgIFZlbm5fbGlzdFtbaV1dIDwtIHZlbm4uZGlhZ3JhbShpbm5lcl9saXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IG15X2ZpbGwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVuYW1lID0gVmVubl9maWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gMC4wNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSBWZW5uX3RpdGxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbi5jZXggPSAxLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluLmZvbnRmYWNlID0gImJvbGQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbi5mb250ZmFtaWx5ID0gInNhbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbi5wb3MgPSBjKDAuNSwgMS4wNSkKICAgICAgICApCiAgICB9CiAgfQogIHJtKGlubmVyX2xpc3QsIFZlbm5fdGl0bGUsIFZlbm5fZmlsZSkKICBpZihrZWVwX3RpbWUpIHRvYygpCn0KCmlmKHBsYXlfYXVkaW8pIGJlZXAoc291bmQgPSA2KQpgYGAKTG9va2luZyBhdCB0aGUgVmVubnMgaXQgY2FuIGJlIHNlZW4gdGhhdCB0aGUgc3BhcnNlc3QgbmV0d29ya3MgYXJlIGFsbW9zdCBhbHdheXMgcHJvZHVjZWQgYnkgU1BJRUMtRUFTSSBhbmQgdGhhdCB0aGUgbnVtYmVyIG9mIGVkZ2VzIHNoYXJlZCBieSBhbGwgbWV0aG9kcyBmb3IgYW55IGdpdmVuIGlzIHZlcnkgbG93LiAKCiMgQ29tcGFyaW5nIGdsb2JhbCBwcm9wZXJ0aWVzLgoKQ29tcGFyaW5nIGdsb2JhbCBuZXR3b3JrIHByb3BlcnRpZXMgbWF5IGJlIG9mIGFzc2lzdGFuY2UgaW4gZXZhbHVhdGluZyB0aGUgZWZmZWN0IG9mIHRoZSBpbmZlcmVuY2UgbWV0aG9kIG9yIG9mIHRoZSB0eXBlIG9mIHN0dWR5LiBBbHRob3VnaCBOZXRDb01pIG9mZmVycyB2ZXJ5IGVmZmVjdGl2ZSB0b29scyB0byBjb21wYXJlIHR3byBuZXR3b3JrcywgaXQgZG9lcyBub3QgZWFzaWx5IGdlbmVyYWxpemUgdG8gbW9yZSB0aGFuIDItMyBuZXR3b3Jrcy4gSGVyZSwgSSB3aWxsIHVzZSB0aGUgZ2xvYmFsX2FsbF9kZiBhbmQgZ2xvYmFsX2xjY19kZiBhbmQgcHJvZHVjZSBncmFwaHMgdXNpbmcgUENBLiAgCl9fTm90ZV9fIGxpbmUgMTA1MSBzaG91bGQgYmUgbWFudWFsbHkgYWRhcHRlZCB0byBzaG93IGFsbCBsb2FkaW5ncyBhbmQgc2NvcmVzCgpgYGB7ciBjb21wYXJlX2dsb2JhbCwgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJDb21wYXJpbmcgZ2xvYmFsIHByb3BlcnRpZXMiKQojIGNyZWF0ZSBhIGxhYmVsIGFuZCBleHRyYWN0IG1hdHJpeCBmb3IgdGhlIGFuYWx5c2lzCmdsb2JhbF9hbGxfZGZfMiA8LSBnbG9iYWxfYWxsX2RmICU+JQogIHRpZHlyOjpzZXBhcmF0ZShkYXRhc2V0LCBpbnRvID0gYygic3R1ZHkiLCAidHlwZSIsICJvYmplY3QiKSwgcmVtb3ZlID0gRikgJT4lCiAgc2VsZWN0KC10eXBlLCAtb2JqZWN0KSAlPiUKICBtdXRhdGUobWV0aG9kX2JyaWVmID0gY2FzZV93aGVuKAogICAgbWV0aG9kID09ICJzcGllY2Vhc2kiIH4gInNwaSIsCiAgICBtZXRob2QgPT0gInNwcmluZyIgfiAic3ByIiwKICAgIG1ldGhvZCA9PSAiY2NyZXBlIiB+ICJjY3IiLAogICAgbWV0aG9kID09ICJzcGFyY2MiIH4gInNwYSIKICApKSAlPiUKICB0aWR5cjo6dW5pdGUobGFiZWwsIHN0dWR5LCBtZXRob2RfYnJpZWYsIHNlcCA9ICJfIiwgcmVtb3ZlID0gRikKCgojIGtlZXAgb25seSByZWxldmFudCBjb2x1bW5zIGFuZCBtYWtlIGEgbWF0cml4Cmdsb2JhbF9hbGxfZGZfbWF0IDwtIGdsb2JhbF9hbGxfZGZfMiAlPiUgCiAgZHBseXI6OnNlbGVjdChsYWJlbCwgbkNvbXAxOm5uZWdlZGdlLCBzYW1wbGVzLCBDaGFvMSwgUGllbG91X0osIGF2ZV9CQykgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJsYWJlbCIpICU+JSBhcy5tYXRyaXgoKQoKIyBvcHRpb25hbGx5IG9idGFpbiBhIHNjYXR0ZXJwbG90IG1hdHJpeAppZih2ZXJib3NlX291dHB1dCkgZ2dwYWlycyhhcy5kYXRhLmZyYW1lKGdsb2JhbF9hbGxfZGZfbWF0KSwgcHJvZ3Jlc3MgPSBGKQoKCiMgbG9vayBhdCB0aGUgbnVtYmVyIG9mIGNvbXBvbmVudHMgKHVzaW5nIGNvcnJlbGF0aW9uIG1hdHJpeCkKdmFyX3RvX3VzZSA8LSAhKGNvbG5hbWVzKGdsb2JhbF9hbGxfZGZfbWF0KSAlaW4lIGMoIm5jb21wIiwgInZlcnRDb25uZWN0MSIsImVkZ2VDb25uZWN0MSIsIm50YXhhIikpCnBzeWNoOjpmYS5wYXJhbGxlbChnbG9iYWxfYWxsX2RmX21hdFssYygxOjUsODoxNCwxNzoxOCldLCBmYSA9ICJwYyIsIG4uaXRlciA9IDEwMCkKCgpQQ0ExIDwtIHBzeWNoOjpwcmluY2lwYWwoZ2xvYmFsX2FsbF9kZl9tYXRbLGMoMTo1LDg6MTQsMTc6MTgpXSwgbmZhY3RvcnMgPSAyLCByb3RhdGUgPSAidmFyaW1heCIpClBDQTEKYmlwbG90KFBDQTEsIHhsaW0ucyA9IGMoLTEsNCksIHlsaW0ucyA9IGMoLTIsNSksIG1haW4gPSAiUENBIGJpcGxvdCwgYWxsIHZhcmlhYmxlcyIpCmZ1bGxuZXRzY29yZXMgPC0gY2JpbmQoZ2xvYmFsX2FsbF9kZl8yLFBDQTEkc2NvcmVzKQojIHRoZSBzY29yZSBwbG90CmdncGxvdChmdWxsbmV0c2NvcmVzLCBtYXBwaW5nID0gYWVzKHggPSBSQzEsIHkgPSBSQzIsIHNoYXBlID0gbWV0aG9kLCBjb2xvdXIgPSBzdHVkeSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAiYWxsIHZhcmlhYmxlcyIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygic3BpZWNlYXNpIiA9IDE2LCAic3ByaW5nIiA9IDE3LCAiY2NyZXBlIiA9IDE1LCAic3BhcmNjIiA9IDE4KSkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlBhaXJlZCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnZhcl90b191c2UgPC0gY29sbmFtZXMoZ2xvYmFsX2FsbF9kZl9tYXQpICVpbiUgYygibmNvbXAxIiwgIm1vZHVsYXJpdHkxIiwiZGVuc2l0eTEiLCJjbHVzdENvZWYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhdlBhdGgxIiwgInBlcDEiLCAiUGllbG91X0oiLCAiYXZlX0JDIiwgIm5ub2RlcyIpCmNhdCgidmFyaWFibGVzIHRvIHVzZTogIiwgdmFyX3RvX3VzZSwgIlxuIikKCnBzeWNoOjpmYS5wYXJhbGxlbChnbG9iYWxfYWxsX2RmX21hdFssdmFyX3RvX3VzZV0sIGZhID0gInBjIiwgbi5pdGVyID0gMTAwKQoKUENBMiA8LSBwcmluY2lwYWwoZ2xvYmFsX2FsbF9kZl9tYXRbLHZhcl90b191c2VdLCBuZmFjdG9ycyA9IDIsIHJvdGF0ZSA9ICJ2YXJpbWF4IikKUENBMgpiaXBsb3QoUENBMikKZnVsbG5ldHNjb3Jlc18yIDwtIGNiaW5kKGdsb2JhbF9hbGxfZGZfMixQQ0EyJHNjb3JlcykKdmFyX2FjY19SQzEgPC0gcm91bmQoUENBMiRWYWNjb3VudGVkWzQsMV0sMykKdmFyX2FjY19SQzIgPC0gcm91bmQoUENBMiRWYWNjb3VudGVkWzQsMl0sMykKIyB0aGUgc2NvcmUgcGxvdApnZ3Bsb3QoZnVsbG5ldHNjb3Jlc18yLCBtYXBwaW5nID0gYWVzKHggPSBSQzEsIHkgPSBSQzIsIHNoYXBlID0gbWV0aG9kLCBjb2xvdXIgPSBzdHVkeSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAic2VsZWN0ZWQgdmFyaWFibGVzIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJzcGllY2Vhc2kiID0gMTYsICJzcHJpbmciID0gMTcsICJjY3JlcGUiID0gMTUsICJzcGFyY2MiID0gMTgpKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiUGFpcmVkIikgKwogIGxhYnMoeCA9IHBhc3RlKCJSQzEgKCIsIHZhcl9hY2NfUkMxLCAiKSIsIHNlcCA9IiIpLAogICAgICAgeCA9IHBhc3RlKCJSQzIgKCIsIHZhcl9hY2NfUkMyLCAiKSIsIHNlcCA9IiIpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCgoKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUoZmlsZS5wYXRoKG91dHB1dF9mb2xkZXIsb3V0X2ZpbGVuYW1lX3ByZWYpLCAiX1BDQS50aWZmIixzZXA9IiIpLCBkZXZpY2UgPSAidGlmZiIsIHdpZHRoID0gNiwgCiAgICAgICBoZWlnaHQgPSA1LCB1bml0cyA9ICJpbiIsIGRwaT02MDApCgppZihrZWVwX3RpbWUpIHRvYygpCmBgYAoKV2l0aCB0aGVzZSBtYW55IGRhdGFzZXRzIGFuZCBpbmZlcmVuY2UgbWV0aG9kcyB0aGUgUENBIGJlY29tZXMgZGlmZmljdWx0IHRvIHVzZS4gTm90ZSBob3cgdGhlIHByb3BlcnRpZXMgb2YgdGhlIG5ldHdvcmsgYXJlLCBhcyBleHBlY3RlZCwgc3BlY2lmaWMgdG8gYW55IGdpdmVuIGluZmVyZW5jZSBtZXRob2RzIGFuZCBkYXRhLXNldCBhbmQgaG93IG1ldGhvZHMgYmFzZWQgb24gY29ycmVsYXRpb24gKHNwYXJDQyBhbmQgQ0NSRVBFKSB0ZW5kIHRvIGJlIGNsb3NlciBhbW9uZyB0aGVtIHRoYW4gdG8gbWV0aG9kcyBiYXNlZCBvbiBjb25kaXRpb25hbCBkZXBlbmRlbmNlLiAKClRoZSBhbmFseXNpcyBpcyByZXBlYXRlZCBiZWxvdyBvbiB0aGUgbGFyZ2VzdCBjb25uZWN0ZWQgY29tcG9uZW50IGZvciBlYWNoIG5ldHdvcmsuICAKCmBgYHtyIGNvbXBhcmVfZ2xvYmFsX0xDQywgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJDb21wYXJpbmcgZ2xvYmFsIHByb3BlcnRpZXMgb24gTENDIikKIyBjcmVhdGUgYSBsYWJlbCBhbmQgZXh0cmFjdCBtYXRyaXggZm9yIHRoZSBhbmFseXNpcwpnbG9iYWxfYWxsX2RmXzMgPC0gZ2xvYmFsX2xjY19kZiAlPiUKICB0aWR5cjo6c2VwYXJhdGUoZGF0YXNldCwgaW50byA9IGMoInN0dWR5IiwgInR5cGUiLCAib2JqZWN0IiksIHJlbW92ZSA9IEYpICU+JQogIHNlbGVjdCgtdHlwZSwgLW9iamVjdCkgJT4lCiAgbXV0YXRlKG1ldGhvZF9icmllZiA9IGNhc2Vfd2hlbigKICAgIG1ldGhvZCA9PSAic3BpZWNlYXNpIiB+ICJzcGkiLAogICAgbWV0aG9kID09ICJzcHJpbmciIH4gInNwciIsCiAgICBtZXRob2QgPT0gImNjcmVwZSIgfiAiY2NyIiwKICAgIG1ldGhvZCA9PSAic3BhcmNjIiB+ICJzcGEiCiAgKSkgJT4lCiAgdGlkeXI6OnVuaXRlKGxhYmVsLCBzdHVkeSwgb2JqX3R5cGUsIG1ldGhvZF9icmllZiwgc2VwID0gIl8iLCByZW1vdmUgPSBGKQoKCiMga2VlcCBvbmx5IHJlbGV2YW50IGNvbHVtbnMgYW5kIG1ha2UgbWF0cml4Cmdsb2JhbF9hbGxfZGZfbWF0XzIgPC0gZ2xvYmFsX2FsbF9kZl8zICU+JSAKICBkcGx5cjo6c2VsZWN0KGxhYmVsLCBsY2NTaXplMTpubmVnZWRnZSwgc2FtcGxlcywgQ2hhbzEsIFBpZWxvdV9KLCBhdmVfQkMpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygibGFiZWwiKSAlPiUgYXMubWF0cml4KCkKIyBuZWVkIHRvIHJlbW92ZSBhbiBJbmYgdmFsdWUsIEkgYW0gcmVtb3ZpbmcgdGhlIHJvdwoKIyBvYnRhaW4gYSBzY2F0dGVycGxvdCBtYXRyaXgKaWYodmVyYm9zZV9vdXRwdXQpIGdncGFpcnMoYXMuZGF0YS5mcmFtZShnbG9iYWxfYWxsX2RmX21hdF8yWy0zLF0pLCBwcm9ncmVzcyA9IEYpCgoKIyBsb29rIGF0IHRoZSBudW1iZXIgb2YgY29tcG9uZW50cyAodXNpbmcgY29ycmVsYXRpb24gbWF0cml4KTogbXVzdCBleGNsdWRlCiMgYXZlIHBhdGggbGVuZ3RoIGFuZCBjbHVzdGVyaW5nIGNvZWZmaWNpZW50IHdoaWNoIGNvbnRhaW4gSW5mIGFuZCBOYU4KCnBzeWNoOjpmYS5wYXJhbGxlbChnbG9iYWxfYWxsX2RmX21hdF8yWy0zLGMoMTozLDYsOToxNCwxNzoxOCldLCBmYSA9ICJwYyIsIG4uaXRlciA9IDEwMCkKCgpQQ0ExX2xjYyA8LSBwc3ljaDo6cHJpbmNpcGFsKGdsb2JhbF9hbGxfZGZfbWF0XzJbLTMsYygxOjMsNiw5OjE0LDE3OjE4KV0sIG5mYWN0b3JzID0gMiwgcm90YXRlID0gInZhcmltYXgiKQpQQ0ExX2xjYwpiaXBsb3QoUENBMV9sY2MsIG1haW4gPSAiUENBIGJpcGxvdCwgYWxsIHZhcmlhYmxlcyIpCmZ1bGxuZXRzY29yZXNfbGNjIDwtIGNiaW5kKGdsb2JhbF9hbGxfZGZfM1stMyxdLFBDQTFfbGNjJHNjb3JlcykKIyB0aGUgc2NvcmUgcGxvdApnZ3Bsb3QoZnVsbG5ldHNjb3Jlc19sY2MsIG1hcHBpbmcgPSBhZXMoeCA9IFJDMSwgeSA9IFJDMiwgc2hhcGUgPSBtZXRob2QsIGNvbG91ciA9IHN0dWR5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJhbGwgdmFyaWFibGVzIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJzcGllY2Vhc2kiID0gMTYsICJzcHJpbmciID0gMTcsICJjY3JlcGUiID0gMTUsICJzcGFyY2MiID0gMTgpKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiUGFpcmVkIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQoKdmFyX3RvX3VzZV9sY2MgPC0gY29sbmFtZXMoZ2xvYmFsX2FsbF9kZl9tYXQpICVpbiUgYygibGNjU2l6ZTEiLCAibW9kdWxhcml0eTEiLCJkZW5zaXR5MSIsICJwZXAxIiwgIlBpZWxvdV9KIiwgImF2ZV9CQyIsICJubm9kZXMiKQpjYXQoInZhcmlhYmxlcyB0byB1c2U6ICIsIHZhcl90b191c2VfbGNjLCAiXG4iKQoKcHN5Y2g6OmZhLnBhcmFsbGVsKGdsb2JhbF9hbGxfZGZfbWF0XzJbLTMsdmFyX3RvX3VzZV9sY2NdLCBmYSA9ICJwYyIsIG4uaXRlciA9IDEwMCkKClBDQTJfbGNjIDwtIHByaW5jaXBhbChnbG9iYWxfYWxsX2RmX21hdF8yWy0zLHZhcl90b191c2VfbGNjXSwgbmZhY3RvcnMgPSAyLCByb3RhdGUgPSAidmFyaW1heCIpClBDQTJfbGNjCmJpcGxvdChQQ0EyX2xjYykKZnVsbG5ldHNjb3Jlc18yX2xjYyA8LSBjYmluZChnbG9iYWxfYWxsX2RmXzJbLTMsXSxQQ0EyX2xjYyRzY29yZXMpCiMgdGhlIHNjb3JlIHBsb3QKdmFyX2FjY19SQzFfbGNjIDwtIHJvdW5kKFBDQTIkVmFjY291bnRlZFs0LDFdLDMpCnZhcl9hY2NfUkMyX2xjYyA8LSByb3VuZChQQ0EyJFZhY2NvdW50ZWRbNCwyXSwzKQojIHRoZSBzY29yZSBwbG90CmdncGxvdChmdWxsbmV0c2NvcmVzXzJfbGNjLCBtYXBwaW5nID0gYWVzKHggPSBSQzEsIHkgPSBSQzIsIHNoYXBlID0gbWV0aG9kLCBjb2xvdXIgPSBzdHVkeSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnModGl0bGUgPSAic2VsZWN0ZWQgdmFyaWFibGVzIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJzcGllY2Vhc2kiID0gMTYsICJzcHJpbmciID0gMTcsICJjY3JlcGUiID0gMTUsICJzcGFyY2MiID0gMTgpKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAiUGFpcmVkIikgKwogIGxhYnMoeCA9IHBhc3RlKCJSQzEgKCIsIHZhcl9hY2NfUkMxX2xjYywgIikiLCBzZXAgPSIiKSwKICAgICAgIHggPSBwYXN0ZSgiUkMyICgiLCB2YXJfYWNjX1JDMl9sY2MsICIpIiwgc2VwID0iIikpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlKGZpbGUucGF0aChvdXRwdXRfZm9sZGVyLG91dF9maWxlbmFtZV9wcmVmKSwgIl9QQ0FfbGNjLnRpZmYiLHNlcD0iIiksIGRldmljZSA9ICJ0aWZmIiwgd2lkdGggPSA2LCAKICAgICAgIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwgZHBpPTYwMCkKCmlmKGtlZXBfdGltZSkgdG9jKCkKYGBgCgpUaGUgcmVzdWx0cyBhcmUgc2ltaWxhciwgYW5kIHRoZXJlIGlzIG5vdCBtdWNoIHRvIGFkZC4gV2hpbGUgU1BJRUMtRUFTSSBwcm9kdWNlcyB0aGUgbW9zdCBzcGFyc2UgbmV0d29ya3MgKGFuZCBjYW4gYmUgYSBjYW5kaWRhdGUgZm9yIHRoZSBkZXRlY3Rpb24gb2YgdGhlIG1vc3QgcGFyc2ltb25pb3VzIGludGVyYWN0aW9uIHNldHMpLCBDQ1JFUEUgYW5kIFNwYXJDQyBib3RoIG1heSBkZXRlY3QgaW5kaXJlY3QgaW50ZXJhY3Rpb25zIGFuZCwgZGV0ZWN0aW5nIGNsdXN0ZXJzIGluIHRoaXMgbmV0d29ya3MgbWF5IGJlIHVzZWZ1bCB0byBkZXRlY3Qgc3ViLWJpb21lcyB3aXRoaW4gYSBnaXZlbiBzdHVkeS4KCiAKIyBQbG90dGluZyBzZWxlY3RlZCBuZXR3b3Jrcy4gIAoKSSB3aWxsIG5vdyB1c2UgdGlkeWdyYXBoIGFuZCBnZ3JhcGggdG8gcGxvdCB0aGUgbmV0d29ya3MuIFRoZSB0aGlja25lc3Mgb2YgdGhlIGVkZ2VzIGlzIG1hZGUgcHJvcG9ydGlvbmFsIHRvIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiB0aGUgYXNzb2NpYXRpb24gbWVhc3VyZS4gQ29wcmVzZW5jZSBlZGdlcyBhcmUgaW4gZ3JlZW4sIG11dHVhbCBleGNsdXNpb24gb25lcyBpbiByZWQuIFNpemUgb2YgdGhlIG5vZGVzIGlzIG1hZGUgcHJvcG9ydGlvbmFsIHRvIHRoZSB0b3RhbCBkZWdyZWUgKG5lZ2F0aXZlIGRlZ3JlZSArIHBvc2l0aXZlIGRlZ3JlZSkuIFRoZSBjb2xvciBvZiB0aGUgbm9kZXMgaXMgZGV0ZXJtaW5lZCBieSB0aGUgcGh5bHVtLiBTb21lIG9mIHRoaXMgb3B0aW9ucyBjYW4gYmUgYWRqdXN0ZWQgaW4gdGhlIGNhbGwgdG8gdGhlIHBsb3RfZ2dyYXBoKCkgZnVuY3Rpb24gYmVsb3cuIE90aGVycyBtdXN0IGJlIGFkanVzdGVkIGluIHRoZSBjb2RlIG9mIHRoZSBmdW5jdGlvbiAoYWxsIGZ1bmN0aW9ucyBhcmUgaW4gdGhlIHNvdXJjZSBmb2xkZXIpLiAgCgpgYGB7ciBwbG90dGluZ19uZXR3b3JrcywgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJQbG90dGluZyB0aGUgbmV0d29ya3Mgd2l0aCBnZ3JhcGgiKQojIGNyZWF0ZSBhIGxpc3Qgb2YgbmV0d29yayBwbG90cwpuZXRwbG90X2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHRpZHlncmFwaF9saXN0X3dzdGF0cykpCmZvcihpIGluIHNlcV9hbG9uZyh0aWR5Z3JhcGhfbGlzdF93c3RhdHMpKXsKICBuZXRwbG90X2xpc3RfMiA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBsZW5ndGgodGlkeWdyYXBoX2xpc3Rfd3N0YXRzW1tpXV0pKQogIGR0c3QgPC0gbmFtZXModGlkeWdyYXBoX2xpc3Rfd3N0YXRzKVtpXQogIGZvcihqIGluIHNlcV9hbG9uZyh0aWR5Z3JhcGhfbGlzdF93c3RhdHNbW2ldXSkpewogICAgaWYoIWlzLnRibF9ncmFwaCh0aWR5Z3JhcGhfbGlzdF93c3RhdHNbW2ldXVtbal1dKSl7CiAgICAgIG5ldHBsb3RfbGlzdF8yW1tqXV0gPC0gIm5vIHBsb3QgdG8gcmV0dXJuIgogICAgICBuZXh0CiAgICB9IGVsc2UgewogICAgICB0ZyA8LSB0aWR5Z3JhcGhfbGlzdF93c3RhdHNbW2ldXVtbal1dCiAgICAgIG10aGQgPC0gbmFtZXModGlkeWdyYXBoX2xpc3Rfd3N0YXRzW1tpXV0pW2pdCiAgICAgICMgdGhlIGRlZmF1bHQgZm9yIHRoZSBhcmd1bWVudCBjMGwwciBpcyBwaHlsdW0sIG90aGVyd2lzZQogICAgICAjIHVzZSBjMGwwciA9IGNsdXN0X21lbWIKICAgICAgIyBhcmd1bWVudCBscCA9ICJib3R0b20iIGlzIHRoZSBkZWZhdWx0OyAicmlnaHQiIGlzIGFuIGFsdGVybmF0aXZlCiAgICAgIG5ldHBsb3RfbGlzdF8yW1tqXV0gPC0gcGxvdF9nZ3JhcGgodGlkeV9ncmFwaCA9IHRnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9IG10aGQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9IGR0c3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHAgPSAicmlnaHQiKQogICAgICBuYW1lcyhuZXRwbG90X2xpc3RfMilbal0gPC0gbXRoZAogICAgICBwcmludChuZXRwbG90X2xpc3RfMltbal1dKQogICAgfQogIH0KICBuZXRwbG90X2xpc3RbW2ldXSA8LSBuZXRwbG90X2xpc3RfMgogIG5hbWVzKG5ldHBsb3RfbGlzdClbaV0gPC0gZHRzdAp9CgpybSh0ZywgZHRzdCwgbXRoZCwgbmV0cGxvdF9saXN0XzIpCmlmKHBsYXlfYXVkaW8pIGJlZXAoc291bmQgPSA2KQppZihrZWVwX3RpbWUpIHRvYygpCmBgYAoKRHVlIHRvIHRoZSBsYXJnZSBudW1iZXIgb2YgaW50ZXJhY3Rpb25zIGl0IGlzIGhhcmQgdG8gZGlzY3VzcyB0aGUgcmVzdWx0cy4gSW4gc29tZSBvZiB0aGUgZ3JhcGhzIGNsdXN0ZXJzIGFyZSBjbGVhcmx5IGV2aWRlbnQgKGFuZCBjb3VsZCBiZSBoaWdobGlnaHRlZCBieSB1c2luZyB0aGUgY2x1c3RlciBpZCBmb3IgY29sb3JzKS4gVGhlIHN0cnVjdHVyZSBvZiB0aGUgZ3JhcGhzIG9idGFpbmVkIHdpdGggdGhlIGRpZmZlcmVudCBtZXRob2RzIGZvciBlYWNoIGdpdmVuIGRhdGFzZXQgaXMgYWxzbyBkaWZmZXJlbnQuIFRoaXMgbWF5IGJlIGR1ZSB0byB0aGUgZmFjdCB0aGF0IGJvdGggdGhlIGNvcnJlbGF0aW9uIGJhc2VkIG1ldGhvZHMgbWF5IGRldGVjdCBpbmRpcmVjdCBpbnRlcmFjdGlvbnMuICAKSW4gYWRkaXRpb24sIGdpdmVuIHRoZSBzY29wZSBvZiB0aGlzIGFuYWx5c2lzICh3aXRoIGVtcGhhc2lzIG9uIG5vZGVzIGFuZCBlZGdlcyBjb25zZXJ2ZWQgYWNyb3NzIGRpZmZlcmVudCBzdHVkaWVzKSB0aGVyZSBpcyBsaXR0bGUgcG9pbnQgaW4gZGlzY3Vzc2luZyBpbmRpdmlkdWFsIG5ldHdvcmtzLCBiZWNhdXNlIHRoaXMgd291bGQgcmVxdWlyZSBnb2luZyBpbnRvIGRldGFpbCBpbiB0aGUgZXhwZXJpbWVudGFsIGFwcHJvYWNoIHVzZWQgaW4gZWFjaCBzdHVkeS4gIApUaGUgZm9sbG93aW5nIGNodW5rIGlkIGRlc2lnbiB0byBjb21wYXJlIG5ldHdvcmsgcGxvdHMgYnVpbGQgd2l0aCBkaWZmZXJlbnQgbWV0aG9kcyBpbiBhIGdyaWQuIFR3byBvZiB0aGUgZGF0YXNldHMgKGNob3NlbiBiZWNhdXNlIHRoZXkgcmV0dXJuZWQgYSBuZXR3b3JrIGZvciBhbGwgaW5mZXJlbmNlIG1ldGhvZHMgYW5kIGJlY2F1c2UgdGhleSBoYWQgbmV0d29ya3Mgb2YgZGlmZmVyZW50IGNvbXBsZXhpdHkgaGF2ZSBiZWVuIGNob3NlbiBoZXJlKS4gSG93ZXZlciwgZHVlIHRvIHRoZSBsYXJnZSBudW1iZXIgb2YgZWxlbWVudHMgaW4gdGhlIGdyYXBoLCBwbG90dGluZyBhbGwgdGhlIGVsZW1lbnRzIChncmFwaCwgbGVnZW5kLCB0aXRsZSkgaXMgZGlmZmljdWx0IGFuZCBtYXkgcmVxdWlyZSBhY3Rpbmcgb24gZ3JhcGhpYyBwYXJhbWV0ZXJzLiAgCgpgYGB7ciBjb21wYXJlX25ldHdvcmtfcGxvdHMsIGRwaSA9IDk2LCBldmFsID0gRn0KaWYoa2VlcF90aW1lKSB0aWMoIlBsb3R0aW5nIGFuZCBzYXZpbmcgbmV0d29ya3MgaW4gYSBncmlkIikKCnAxPC1uZXRwbG90X2xpc3RbWzJdXVtbMV1dCnAyPC1uZXRwbG90X2xpc3RbWzJdXVtbMl1dCnAzPC1uZXRwbG90X2xpc3RbWzJdXVtbM11dCnA0PC1uZXRwbG90X2xpc3RbWzJdXVtbNF1dCmdncmlkXzEgPC0gZWdnOjpnZ2FycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5yb3cgPSAyLCBuY29sID0gMikKZm4gPC0gcGFzdGUoZmlsZS5wYXRoKG91dHB1dF9mb2xkZXIsb3V0X2ZpbGVuYW1lX3ByZWYpLCAiX1NUMTEwLnRpZmYiLHNlcD0iIikKZ2dzYXZlKGdncmlkXzEsIGZpbGVuYW1lID0gZm4sIGRwaSA9IDYwMCwgd2lkdGggPSA3LCBoZWlnaHQgPSA3KQpnZ3JpZF8xCgpwNTwtbmV0cGxvdF9saXN0W1s5XV1bWzFdXQpwNjwtbmV0cGxvdF9saXN0W1s5XV1bWzJdXQpwNzwtbmV0cGxvdF9saXN0W1s5XV1bWzNdXQpwODwtbmV0cGxvdF9saXN0W1s5XV1bWzRdXQpnZ3JpZF8yIDwtIGVnZzo6Z2dhcnJhbmdlKHA1LCBwNiwgcDcsIHA4LCBucm93ID0gMiwgbmNvbCA9IDIpCmZuIDwtIHBhc3RlKGZpbGUucGF0aChvdXRwdXRfZm9sZGVyLG91dF9maWxlbmFtZV9wcmVmKSwgIl9TVDEzNi50aWZmIixzZXA9IiIpCmdnc2F2ZShnZ3JpZF8yLCBmaWxlbmFtZSA9IGZuLCBkcGkgPSA2MDAsIHdpZHRoID0gNywgaGVpZ2h0ID0gNykKZ2dyaWRfMgoKaWYocGxheV9hdWRpbykgYmVlcChzb3VuZCA9IDYpCmlmKGtlZXBfdGltZSkgdG9jKCkKcm0ocDEsIHAyLCBwMywgcDQsIHA1LCBwNiwgcDcsIHA4KQpgYGAKCgojIE5vZGUgYW5hbHlzaXMuCgpXaGVuIGNvbXBhcmluZyBuZXR3b3JrcyBmcm9tIHNldmVyYWwgc3R1ZGllcyBpdCBtaWdodCBiZSBvZiBpbnRlcmVzdCB0byBjbGFzc2lmeSBub2RlcyBvbiB0aGUgYmFzaXMgb2YgdGhlaXIgbWVhc3VyZXMgb2YgY2VudHJhbGl0eSBhY3Jvc3MgbXVsdGlwbGUgc3R1ZGllczogaXMgdGhlcmUgYSBzdXBlciBodWIgKGkuZS4gYSBub2RlIHdoaWNoIGkgYSBodWIgaW4gc2V2ZXJhbCBuZXR3b3Jrcyk/IEhvdyBhcmUgZGlmZmVyZW50IGNlbnRyYWxpdHkgbWVhc3VyZXMgcmVsYXRlZD8gQXJlIHRoZXJlIG5vZGVzIHdoaWNoIGVuZ2FnZSBtb3JlIG9mdGVuIGluIG5lZ2F0aXZlIGludGVyYWN0aW9ucz8gT2YgY291cnNlIGl0IHdvdWxkIGJlIHBvaW50bGVzcyB0byBjb21wYXJlIGFjcm9zcyBzZXZlcmFsIG1ldGhvZHMgb2YgaW5mZXJlbmNlIG9yLCBnaXZlbiB0aGUgcmVzdWx0cyBhYm92ZSwgYmV0d2VlbiBBU1YgYW5kIEZNQk4gc3R1ZGllcyAod2hlbiBhZ2dyZWdhdGlvbiBhdCB0aGUgZ2VudXMgbGV2ZWwgaXMgdXNlZCkuClRoaXMgc2NyaXB0IHdpbGwgcHJvZHVjZSBhIGZldyBub2RlIHBsb3RzYXMgYSBwcm9vZiBvZiBjb25jZXB0LiBCb3RoIFNQSUVDLUVBU0kgYW5kIFNwYXJDQyB3aWxsIGJlIHVzZWQgb24gZGF0YSBleHRyYWN0ZWQgZnJvbSBGTUJOIGFuZCBhZ2dyZWdhdGVkIGF0IHRoZSBnZW51cyBsZXZlbC4KCgoKYGBge3Igbm9kZV9hbmFseXNpc19odWJfZnJlcSwgZHBpID0gOTZ9CmlmKGtlZXBfdGltZSkgdGljKCJOb2RlIGFuYWx5c2lzIikKCm15bWV0aG9kcyA8LSBjKCJzcGllY2Vhc2kiLCJzcGFyY2MiKQpuX3RvX2xhYmVsIDwtIDIwICMgbWF4IG51bWJlciBvZiBub2RlcyB0byBsYWJlbCBpbiB0aGUgbm9kZSBwbG90cwpub2RlX2FuYWx5c2lzX2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKG15bWV0aG9kcykpCmZvcihpIGluIHNlcV9hbG9uZyhteW1ldGhvZHMpKXsKICAjIGNyZWF0ZSB0aGUgZGZzLCBvbmUgZm9yIHNwaWVjLWVhc2kgYW5kIG9uZSBmb3Igc3BhckNDIGFuZCBvbmx5IHNlbGVjdCBGTUJOCiAgIyBvbmx5IHNlbGVjdCBub2RlcyB3aXRoIGRlZ3JlZT4wCiAgRk1CTl9ub2RlX2RmIDwtIG5vZGVfc3RhdHNfZGYgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKG1ldGhvZCA9PSBteW1ldGhvZHNbaV0pICU+JQogICAgZHBseXI6OmZpbHRlcihkZWdyZWU+MCkgJT4lCiAgICBtdXRhdGUoU3R1ZHkgPSBhcy5mYWN0b3IoU3R1ZHkpLAogICAgICAgICAgIHJlbF9wb3NfZGVncmVlID0gcG9zX2RlZ3JlZSAvIHN1bShwb3NfZGVncmVlKSkgJT4lCiAgICBkcGx5cjo6cmVuYW1lKHRsYWJlbCA9IGxhYmVsKQogIAogIAogICMgdXNlIGEgbG9vcCB0byBkbyB0aGUgbm9kZSBkZWdyZWUgZ3JhcGhzLCBwcmludCB0aGVtIGFuZCBwdXQgdGhlbSBpbiBhIGxpc3QKICBub2RlX2RlZ3JlZV9wbG90X2xpc3QgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoPW5sZXZlbHMoRk1CTl9ub2RlX2RmJHN0dWR5KSkKICAKICBmb3IoaiBpbiBzZXFfYWxvbmcobGV2ZWxzKEZNQk5fbm9kZV9kZiRTdHVkeSkpKXsKICAgIGd0aXRsZSA9IHBhc3RlKGxldmVscyhGTUJOX25vZGVfZGYkU3R1ZHkpW2pdLCBteW1ldGhvZHNbaV0sIHNlcCA9ICIsICIpCiAgICB0ZW1wX2RmIDwtIEZNQk5fbm9kZV9kZiAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIoU3R1ZHkgPT1sZXZlbHMoRk1CTl9ub2RlX2RmJFN0dWR5KVtqXSkgJT4lCiAgICAgIG11dGF0ZShkZ3IgPSBwb3NfZGVncmVlICsgbmVnX2RlZ3JlZSkgJT4lCiAgICAgIGFycmFuZ2UoLWRncikgJT4lCiAgICAgIHJvd2lkX3RvX2NvbHVtbigpICU+JSAKICAgICAgbXV0YXRlKHRvX2xhYmVsID0gaWZfZWxzZSgocm93aWQ8PTIwIHwgaXNfaHViKSwgdGxhYmVsLCBOQV9jaGFyYWN0ZXJfKSkKICAgIAogICAgYXZlX2RlZ3JlZSA9IG1lYW4odGVtcF9kZiRkZ3IsIG5hLnJtID0gVCkKICAgIGF2ZV9wb3NfZGVncmVlID0gbWVhbih0ZW1wX2RmJHBvc19kZWdyZWUsIG5hLnJtID0gVCkKICAgICMgY3JlYXRpbmcgdGhlIHBsb3QsIG9ubHkgdGhlIG5hbWVzIG9mIHRoZSB0b3AgMjAgbm9kZXMgKGluIHRlcm1zIG9mIGRlZ3JlZSkKICAgICMgYXJlIHBsb3R0ZWQsIGh1YnMgYXJlIGFsd2F5cyBwbG90dGVkCiAgICBnZ3AgPC0gZ2dwbG90KAogICAgICB0ZW1wX2RmLAogICAgICBtYXBwaW5nID0gYWVzKAogICAgICAgIHggPSBwb3NfZGVncmVlLAogICAgICAgIHkgPSBkZ3IsCiAgICAgICAgbGFiZWwgPSBzdHJfdHJ1bmMoZ2VudXMsIDEyLCBzaWRlID0gImNlbnRlciIpCiAgICAgICkKICAgICkgKwogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBsaW5ldHlwZSA9IDMsIHNlID0gRiwgY29sb3IgPSBJKCJibGFjayIpLCBzaG93LmxlZ2VuZCA9IEYpICsKICAgICAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gcGh5bHVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IHJlbEFidW5kYW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gYmV0d2VlbikpICsKICAgICAgZ2VvbV90ZXh0X3JlcGVsKHNob3cubGVnZW5kID0gRiwgbWF4Lm92ZXJsYXBzID0gMjAsIGFscGhhID0gSSgwLjUpKSArCiAgICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAxKSArCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGF2ZV9kZWdyZWUsIGxpbmV0eXBlID0gMywgc2hvdy5sZWdlbmQgPSBGKSArCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGF2ZV9wb3NfZGVncmVlLCBsaW5ldHlwZSA9IDMsIHNob3cubGVnZW5kID0gRikgKwogICAgICBzY2FsZV9hbHBoYV9jb250aW51b3VzKHJhbmdlID0gYygwLjQsIDEpKSArCiAgICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSw2KSkgKwogICAgICBsYWJzKAogICAgICAgIHggPSAicG9zaXRpdmUgZGVncmVlIiwKICAgICAgICB5ID0gImRlZ3JlZSIsCiAgICAgICAgc2l6ZSA9ICJyZWxhdGl2ZSBhYnVuZGFuY2UiLAogICAgICAgIGFscGhhID0gImJldHdlZW5uZXNzIiwKICAgICAgICB0aXRsZSA9IGd0aXRsZQogICAgICApICsKICAgICAgdGhlbWVfYncoKSArCiAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQogICAgCiAgICBwcmludChnZ3ApCiAgICBub2RlX2RlZ3JlZV9wbG90X2xpc3RbW2pdXSA8LSBnZ3AKICAgIG5hbWVzKG5vZGVfZGVncmVlX3Bsb3RfbGlzdFtbal1dKSA8LSBndGl0bGUKICB9CiAgZmlsZV9uYW1lIDwtIHBhc3RlKGZpbGUucGF0aChvdXRwdXRfZm9sZGVyLG91dF9maWxlbmFtZV9wcmVmKSwgIl8iLCBteW1ldGhvZHNbaV0sICJfbm9kZXBsb3RzLlJkYXRhIixzZXA9IiIpCiAgc2F2ZShub2RlX2RlZ3JlZV9wbG90X2xpc3QsIGZpbGUgPSBmaWxlX25hbWUpCiAgCiAgIyBjYWxjdWxhdGUgZnJlcXVlbmN5IGZvciBodWJzIChtZWFuaW5nZnVsIG9ubHkgaWYgeW91IGhhdmUgbWFueSB0YXhhIGFuZCB0aGUgcGVyY2VudGlsZSBmb3IgaHViIGRldGVjdGlvbiBpcyBsb3csIHNheSAwLjc1LTAuOTApCiAgbl9kYXRhc2V0cyA8LSBkcGx5cjo6bl9kaXN0aW5jdChGTUJOX25vZGVfZGYkZGF0YXNldCkKICBGTUJOX25vZGVfZGZfaHVicyA8LSBGTUJOX25vZGVfZGYgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGlzX2h1YiA9PSBUKSAlPiUKICAgIGdyb3VwX2J5KHRsYWJlbCwgcGh5bHVtLCBjbGFzcykgJT4lCiAgICBzdW1tYXJpc2UoaHViX2ZyZXEgPSBuKCkvbl9kYXRhc2V0cykgJT4lCiAgICBhcnJhbmdlKC1odWJfZnJlcSkKICBjYXQoIm1ldGhvZCIsIG15bWV0aG9kc1tpXSwgImh1YiBmcmVxdWVuY3kiLCAiXG4iKQogIGhlYWQoRk1CTl9ub2RlX2RmX2h1YnMsIDIwKQogICMgc2F2ZSBlbGVtZW50cyBpbiB0aGUgbGlzdApub2RlX2FuYWx5c2lzX2xpc3RbW2ldXSA8LSBsaXN0KAogIG5vZGVfZGYgPSBGTUJOX25vZGVfZGYsCiAgcGxvdF9saXN0ID0gbm9kZV9kZWdyZWVfcGxvdF9saXN0LAogIG5vZGVfZGZfaHVicyA9IEZNQk5fbm9kZV9kZl9odWJzCikKbmFtZXMobm9kZV9hbmFseXNpc19saXN0KVtpXTwtIG15bWV0aG9kc1tpXQogIAp9CgppZihwbGF5X2F1ZGlvKSBiZWVwKHNvdW5kID0gNikKaWYoa2VlcF90aW1lKSB0b2MoKQpybShGTUJOX25vZGVfZGYsIG5vZGVfZGVncmVlX3Bsb3RfbGlzdCwgdGVtcF9kZiwgRk1CTl9ub2RlX2RmX2h1YnMpCmBgYAoKVGhlIG5vZGUgcGxvdHMgcHJvdmlkZSBhIG51bWJlciBvZiB2aXN1YWwgY3VlczoKCiogdGhlIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGRvdHRlZCBsaW5lcyBzaG93IHRoZSBhdmVyYWdlIHZhbHVlIGZvciBkZWdyZWUgYW5kIHBvc2l0aXZlIGRlZ3JlZSBhbmQgbWF5IGhlbHAgaW4gaWRlbnRpZnlpbmcgbm9kZXMgd2hvc2UgdmFsdWVzIGFyZSBmYXIgZnJvbSB0aGUgbWVhbjsgIAoKKiB0aGUgZGlhZ29uYWwgY29udGludW91cyBsaW5lIGNvcnJlc3BvbmRzIHRvIHBvaW50cyBmb3Igd2hpY2ggcG9zaXRpdmUgZGVncmVlIGFuZCBkZWdyZWUgYXJlIGVxdWFsOyBwb2ludHMgYWJvdmUgdGhlIGxpbmUgaGF2ZSBhdCBsZWFzdCAxIG5lZ2F0aXZlIGludGVyYWN0aW9uCgoqIHRoZSBkaWFnb25hbCBkb3R0ZWQgbGluZSBpcyB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSBmb3IgZGVncmVlIGFzIGEgZnVuY3Rpb24gb2YgcG9zaXRpdmUgZGVncmVlOyBpdHMgcG9zaXRpb24gYW5kIHNsb3BlIHJlbGF0aXZlIHRvIHRoZSBwcmV2aW91cyBsaW5lIGluZGljYXRlcyBvbiBhdmVyYWdlIGhvdyBtdWNoIHRoZSByYXRpbyBiZXR3ZWVuIHRvdGFsIGRlZ3JlZSBhbmQgcG9zaXRpdmUgZGVncmVlIGNoYW5nZXMgYXMgYSBmdW5jdGlvbiBvZiBkZWdyZWU7IGRhdGEgcG9pbnRzIHdoaWNoIGFyZSBmYXIgZnJvbSB0aGlzIGxpbmUgYWxzbyBoYXZlIGFuIHVudXN1YWwgcmF0aW8gYmV0d2VlbiB0aGUgdHdvIHZhbHVlcyBjb21wYXJlZCB0byB0aGUgb3RoZXIgbm9kZXMgaW4gdGhlIHNhbWUgZGF0YXNldC4gIAoKKiBuZWdhdGl2ZSBodWJzIHdpbGwgYmUgbG9jYXRlZCBjbG9zZSB0byB0aGUgdXBwZXIgbGVmdCBjb3JuZXI7IHBvc2l0aXZlIGh1YnMgd2lsbCBiZSBjbG9zZXIgdG8gdGhlIHJpZ2h0IG9mIHRoZSBwbG90LiAgCgpFdmVuIHdpdGggU1BJRUMtRUFTSSB0aGUgbnVtYmVyIG9mIHBsb3R0ZWQgbm9kZXMgaXMgaGlnaCBmb3Igc29tZSBzdHVkaWVzIChidXQgdmVyeSBsb3cgZm9yIG90aGVycyksIGFuZCBvbmUgbXkgY29uc2lkZXIgcGxvdHRpbmcgYSBnaXZlbiBudW1iZXIgb2Ygbm9kZXMgKHRoZSBvbmVzIHdpdGggdG9wIGRlZ3JlZT8gdG9wIGVpZ2VudmVjdG9yIGNlbnRyYWxpdHk/LCBzb21lIGNvbWJpbmF0aW9uIG9mIGJvdGgpIHRvIHNpbXBsaWZ5IHRoZSBwbG90LiAgClNhaWQgdGhhdCwgYWx0aG91Z2ggYmVldHdlZW5uZXNzIGNlbnRyYWxpdHkgbWF5IGJlIGludGVyZXN0aW5nIHRvIGlkZW50aWZ5ICJib3R0bGVuZWNrIiB0YXhhLCBsb29raW5nIGF0IGJldHdlZW5uZXNzIGlzIGRpZmZpY3VsdCAodGhlIHNoYWRlcyBvZiBhbHBoYSA9IHRyYW5zcGFyZW5jeSBhcmUgdmlzdWFsbHkgZGlmZmljdWx0IHRvIHNlcGFyYXRlKSwgYnV0IG90aGVyd2lzZSBkZWdyZWUsIHBvc2l0aXZlIGRlZ3JlZSBhbmQgYWJ1bmRhbmNlIGFyZSBhbGwgZWFzeSB0byB2aXN1YWxpc2UuICAKCiMgRWRnZSBhbmFseXNpcy4gIAoKRWRnZSBhbmFseXNpcyBvbmx5IG1ha2VzIHNlbnNlIG9uY2Ugb25lIGhhcyBzZXR0bGVkIGZvciBtZWFuaW5nZnVsIHF1ZXN0aW9ucyBhbmQgc2VsZWN0ZWQgYSBsYXJnZSBhbmQgZGl2ZXJzZSBlbm91Z2ggc2V0IG9mIHN0dWRpZXMuClF1ZXN0aW9ucyB3aGljaCBtaWdodCBiZSByZWxldmFudDoKCiogaG93IHN0YWJsZSBpcyBhbiBlZGdlPwoKICArIHdpdGhpbiBhIHN0dWR5IGFjcm9zcyBtZXRob2RzCiAgCiAgKyBhY3Jvc3Mgc3R1ZGllcyB3aXRoIGEgZ2l2ZW4gbWV0aG9kCiAgCiogZ2l2ZW4gYW4gaW5mZXJlbmNlIG1ldGhvZCwgY2FuIG9uZSBhc3NlbWJsZSBhIGNvbnNlbnN1cyBuZXR3b3JrIHVzaW5nIGFzIHdlaWdodHMgdGhlIG51bWJlciBvZiB0aW1lcyBhIGdpdmVuIGVkZ2Ugb2NjdXJzPyBUaGUgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2U/CgoqIGhvdyBpbXBvcnRhbnQgaXMgYW4gZWRnZSAoYXMgbWVhc3VyZWQgYnkgZWRnZSBiZXR3ZWVubmVzcyk/IE9uZSBtdXN0IGNob29zZSBhIG1lYW5pbmdmdWwgY29udGV4dCBmb3IgdGhpcwoKKiBhcmUgY28tb2NjdXJyZW5jZSByZWxhdGlvbnNoaXBzIG1vcmUgZnJlcXVlbnQgYW1vbmcgbWVtYmVycyBvZiB0aGUgc2FtZSBjbGFzcyBvciBmYW1pbHkgKGFuZCB0aGUgcmV2ZXJzZSBmb3IgbXV0dWFsIGV4Y2x1c2lvbik/IFRoaXMgb25seSBtYWtlcyBzZW5zZSB3aXRoaW4gYSBnaXZlbiBtZXRob2QgYW5kIG11c3QgYmUgY29ycmVjdGVkIGJ5IHRoZSBmcmVxdWVuY3kgb2YgZWNoIGdpdmVuIGxhcmdlciB0YXhvbi4KCiogZ2l2ZW4gYSAocG9zc2libHkgaW1wb3J0YW50KSBtaWNyb29yZ2FuaXNtLCB3aXRoIHdoaWNoIG1pY3Jvb3JnYW5pc21zIGlzIGl0IG1vc3QgZnJlcXVlbnRseSBhc3NvY2lhdGVkIHdpdGggcG9zaXRpdmUgb3IgbmVnYXRpdmUgYXNzb2NpYXRpb25zPyBUaGlzIG9ubHkgbWFrZXMgc2Vuc2Ugd2l0aGluIGEgZ2l2ZW4gbWV0aG9kIGFuZCBvbmNlIG9uZSBoYXMgYW5hbHl6ZWQgYSBsYXJnZSBudW1iZXIgb2Ygc3R1ZGllcwoKKiBmb3IgbW9kZXJhdGVseSBsYXJnZSBuZXR3b3Jrcywgd2hpY2ggaXMgdGhlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIG9mIGVkZ2UgYmV0d2Vlbm5lc3M/IERvZXMgdGhpcyBob2xkIGFueSBpbXBvcnRhbmNlIGluIHRlcm1zIG9mIHN0cnVjdHVyZSBvZiB0aGUgbmV0d29ya3M/CgpgYGB7ciBlZGdlX2FuYWx5c2lzX2JldHdlZW5lc3MsIGRwaSA9IDk2fQppZihrZWVwX3RpbWUpIHRpYygiRWRnZSBhbmFseXNpcyIpCiMgdGhpcyBjYWxjdWxhdGVzIHRoZSBlbXBpcmljYWwgcXVhbnRpbGUgb2YgZWRnZSBiZXR3ZWVubmVzcwojIGJ5IGRhdGFzZXQgYW5kIG1ldGhvZDogSSBzdXBwb3NlIG9uZSBjYW4gdGhlbiBzZWxlY3QgdGhvc2UgPjAuOTUKIyB0byBnZXQgYSBzb3J0IG9mIGh1Ym5lc3MgZm9yIGVkZ2VzLCBidXQgaXQgbWFrZXMgc2Vuc2Ugb25seSBmb3IgCiMgbGFyZ2UgbnVtYmVyIG9mIGVkZ2VzCmVkZ2VfbGlzdF9kZiA8LSBlZGdlX2xpc3RfZGYgJT4lIGdyb3VwX2J5KGRhdGFzZXQsIG1ldGhvZCkgJT4lCiAgbXV0YXRlKGVicSA9IGVjZGYoZWRnZV9iZXR3KShlZGdlX2JldHcpKSAlPiUgdW5ncm91cCgpCgojIGNhbGN1bGF0ZSB0aGUgZnJlcXVlbmN5IG9mIGVkZ2VzLCBieSBzdHVkeSBhbmQgdHlwZSBhbmQgCiMgbWVkaWFuIHZhbHVlIGFuZCBJUVIgZm9yIElRUgojIHRoaXMgb25seSBjaGVja3MgZm9yIGNvbnNlcnZlZCBlZGdlcyBhY3Jvc3MgbWV0aG9kcwplZGdlX2ZyZXFfYnlzdHVkeSA8LSBlZGdlX2xpc3RfZGYgJT4lIAogIGdyb3VwX2J5KGRhdGFzZXQsIGFzc29fdHlwZSwgZWRnZV9uYW1lKSAlPiUgCiAgc3VtbWFyaXNlKG4gPSBuKCksCiAgICAgICAgICAgIG1lZF9lYnEgPSBtZWRpYW4oZWJxKSwKICAgICAgICAgICAgaXFyX2VicSA9IElRUihlYnEpKSAlPiUKICBhcnJhbmdlKC1uLCAtbWVkX2VicSwgKSAlPiUgCiAgdW5ncm91cCgpCgojIEkgYW0gbm93IGdlbmVyYXRpbmcgYSBwbG90IG1heSBiZSB3aXRoIHRoZSB0b3AgNTAgZWRnZXMsIG9ubHkgZm9yIEZNQk4gc3R1ZGllcwplZGdlX2ZyZXFfYnlzdHVkeV9GTUJOIDwtIGVkZ2VfZnJlcV9ieXN0dWR5ICU+JQogIGRwbHlyOjpmaWx0ZXIoc3RyX2RldGVjdChkYXRhc2V0LCAiRk1CTiIpKSAlPiUKICBhcnJhbmdlKC1uLCAtbWVkX2VicSkKCiMgbGV0J3MgdHJ5IGEgcGxvdCAoZ2l2ZXMgYW4gZW1waGFzaXMgdG8gbW9zdCBzdGFibGUgZWRnZXMpCnNsaWNlX3RvX3Bsb3QgPC0gc2xpY2UoZWRnZV9mcmVxX2J5c3R1ZHlfRk1CTiwgMTo1MCkgJT4lCiAgICBtdXRhdGUoZWRnZV9uYW1lID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIoZWRnZV9uYW1lLCBtZWRfZWJxKSkgCgpnZ3Bsb3Qoc2xpY2VfdG9fcGxvdCwgCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBlZGdlX25hbWUsIHkgPSBtZWRfZWJxLCBzaXplID0gbiwgY29sb3IgPSBhc3NvX3R5cGUpKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKHggPSAiZWRnZSIsIHkgPSAiZWRnZSBiZXR3ZWVubmVzcyBxdWFudGlsZSIsCiAgICAgIHNpemUgPSAiZWRnZSBmcmVxLiIpICsKICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gYygiZ3JlZW4iLCJyZWQiKSkKYGBgCk1hbnkgb2YgdGhlIG1vc3Qgc3RhYmxlIGVkZ2VzIGluY2x1ZGUgZ3V0IGJhY3RlcmlhIGFuZCBwcm9iYWJseSByZXByZXNlbnQgY2x1c3RlciBvZiBpbnRlcmFjdGlvbnMgZnJvbSB0aGlzIGVudmlyb25tZW50IHdoaWNoIGFyZSBjYXJyaWVkIG92ZXIgdG8gbWlsayBhbmQgdGhlbiwgcG9zc2libHksIGNoZWVzZSAoc29tZSBzdHVkaWVzIGFuYWx5emVkIGhlcmUgaW5jbHVkZSBhbHNvIGVudmlyb25tZW50YWwgc2FtcGxlcywgbGlrZSBmZWNlcykuICAKVGhlIGFuYWx5c2lzIGhlcmUgd2FzIGNhcnJpZWQgb3V0IGJ5IGdyb3BpbmcgYnkgZGF0YXNldCBhbmQgYXNzb2NpYXRpb24gdHlwZSwgdGhlcmVmb3JlIHRoZSBmcmVxdWVuY3kgaXMgYnkgZGF0YXNldCBhbmQgYSBmcmVxdWVuY3kgb2YgNCBpbmRpY2F0ZXMgdGhhdCBhIGdpdmVuIGVkZ2Ugd2FzIGRldGVjdGVkIGJ5IGFsbCA0IG1ldGhvZHMgaW4gYSBzaW5nbGUgZGF0YXNldC4gVGhlIGNvZGUgY2FuIGJlIGVhc2lseSBtb2RpZmllZCB0byBkZXRlY3QgZWRnZXMgd2hpY2ggYXJlIGNvbnNlcnZlZCBhY3Jvc3MgZGlmZmVyZW50IHN0dWRpZXMgYW5kIGRpZmZlcmVudCBtZXRob2RzLgpJIHdpbGwgbm93IHRyeSB0byBlc3RpbWF0ZSB0aGUgdGF4b25vbWljIGFzc29ydGF0aXZpdHkuIFRvIGRvIHRoaXMsIHRoZSBvZGRzIHJhdGlvIChPUikgZm9yIGNvcHJlc2VuY2UgbGlua3MgKGdpdmVuIHRoYXQgdGhlIG5vZGVzIGJlbG9uZyB0byB0aGUgc2FtZSBmYW1pbHkpIGFuZCBtdXR1YWwgZXhsdXNpb24gbGlua3MgKGdpdmVuIHRoYXQgdGhlIG5vZGVzIGJlbG9uZyB0byBkaWZmZXJlbnQgZmFtaWxpZXMpIGlzIGNhbGN1bGF0ZWQgYW5kIHRoZSBzaWduaWZpY2FuY2UgaXMgZXZhbHVhdGVkIHVzaW5nIGBlcGkuMmJ5MigpYCBmdW5jdGlvbi4gIAoKCmBgYHtyIGVkZ2VfYW5hbHlzaXNfYXNzb3J0YXRpdml0eSwgZHBpID0gOTZ9CiMgc2FtZSwgYnkgbWV0aG9kIGFuZCBhc3NvdHlwZSwgb25seSBmb3IgRk1CTgojIG9ubHkgbWVhbmluZ2Z1bCBmb3IgaGlnaCBudW1iZXIgb2Ygc3R1ZGllcwplZGdlX2ZyZXFfYnltZXRob2QgPC0gZWRnZV9saXN0X2RmICU+JSAKICBkcGx5cjo6ZmlsdGVyKHN0cl9kZXRlY3QoZGF0YXNldCwgIkZNQk4iKSkgJT4lCiAgZ3JvdXBfYnkobWV0aG9kLCBhc3NvX3R5cGUpICU+JSAKICBjb3VudChlZGdlX25hbWUpICU+JQogIGFycmFuZ2UobWV0aG9kLCBhc3NvX3R5cGUsIC1uKQoKIyBUaGUgZm9sbG93aW5nIHNlY3Rpb24gZXZhbHVhdGVzIHRheG9ub21pYyBhc3NvcnRhdGl2aXR5IChpLmUuIGV2YWx1YXRlcyBpZiAKIyBjb3ByZXNlbmNlIGFzc29jaWF0aW9ucyBhcmUgbW9yZSBmcmVxdWVudCBhbW9uZyBtZW1iZXJzIG9mIHRoZSBzYW1lIAojIGZhbWlseSwgb3JkZXIgb3IgY2xhc3MpLiBUaGUgb2RkcyByYXRpbyBvZiBjb3ByZXNlbmNlIHJlbGF0aW9uc2hpcHMgd2l0aGluCiMgdGhlIHNhbWUgZmFtaWx5IGlzIGNhbGN1bGF0ZWQgdXNpbmcgZXBpUjo6ZXBpLjJieTIoKQoKIyBmaXJzdCwgYWRkIHRheG9ub215IHRvIHRoZSAKdW5pcXVlX3RheCA8LSBub2RlX3N0YXRzX2RmICU+JSAKICBkcGx5cjo6c2VsZWN0KGxhYmVsLCBkb21haW46c3BlY2llcykgJT4lCiAgZGlzdGluY3QoKQoKZWRnZV9saXN0X2RmX3d0YXhhIDwtIGVkZ2VfbGlzdF9kZgplZGdlX2xpc3RfZGZfd3RheGEgPC0gbGVmdF9qb2luKGVkZ2VfbGlzdF9kZl93dGF4YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdCh1bmlxdWVfdGF4LCBsYWJlbCwgY2xhc3MsIG9yZGVyLCBmYW1pbHkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImZyb21fbmFtZSIgPSAibGFiZWwiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShmcm9tX2NsYXNzID0gY2xhc3MsIGZyb21fb3JkZXIgPSBvcmRlciwgZnJvbV9mYW1pbHkgPSBmYW1pbHkpCmVkZ2VfbGlzdF9kZl93dGF4YSA8LSBsZWZ0X2pvaW4oZWRnZV9saXN0X2RmX3d0YXhhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6c2VsZWN0KHVuaXF1ZV90YXgsIGxhYmVsLCBjbGFzcywgb3JkZXIsIGZhbWlseSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygidG9fbmFtZSIgPSAibGFiZWwiKSkgJT4lCiAgZHBseXI6OnJlbmFtZSh0b19jbGFzcyA9IGNsYXNzLCB0b19vcmRlciA9IG9yZGVyLCB0b19mYW1pbHkgPSBmYW1pbHkpCiMgY2hlY2sgaWYgc2FtZSBmYW1pbHkgb3Igc2FtZSBjbGFzcwplZGdlX2xpc3RfZGZfd3RheGEgPC0gZWRnZV9saXN0X2RmX3d0YXhhICU+JQogIG11dGF0ZShzYW1lX2NsYXNzID0gaWZfZWxzZShmcm9tX2NsYXNzID09IHRvX2NsYXNzLCBULCBGKSwKICAgICAgICAgc2FtZV9vcmRlciA9IGlmX2Vsc2UoZnJvbV9vcmRlciA9PSB0b19vcmRlciwgVCwgRiksCiAgICAgICAgIHNhbWVfZmFtaWx5ID0gaWZfZWxzZShmcm9tX2ZhbWlseSA9PSB0b19mYW1pbHksIFQsIEYpCiAgICAgICAgICkKCiMgdXNlIGEgbG9vcCB0byBjYWxjdWxhdGUgb2RkcyByYXRpb3MgYW5kIHByb2JhYmlsaXRpZXMKIyBnZXQgdGhlIG51bWJlciBvZiBzdHVkaWVzCgplZGdlX2xpc3RfZGZfd3RheGEgPC0gZWRnZV9saXN0X2RmX3d0YXhhICU+JSAKICBzZXBhcmF0ZShkYXRhc2V0LCBpbnRvID0gYygic3R1ZHkiLCAiY29sMiIsICJjb2wzIiksIHJlbW92ZSA9IEYpICU+JQogIHNlbGVjdCgtY29sMiwgLWNvbDMpICU+JQogIG11dGF0ZShzdHVkeSA9IGFzLmZhY3RvcihzdHVkeSksCiAgICAgICAgIHNmID0gZmFjdG9yKHNhbWVfZmFtaWx5LCBsZXZlbHMgPSBjKCJUUlVFIiwgIkZBTFNFIikpLAogICAgICAgICBzbyA9IGZhY3RvcihzYW1lX29yZGVyLCBsZXZlbHMgPSBjKCJUUlVFIiwgIkZBTFNFIikpLAogICAgICAgICBzYyA9IGZhY3RvcihzYW1lX2NsYXNzLCBsZXZlbHMgPSBjKCJUUlVFIiwgIkZBTFNFIikpCiAgKQoKYXNzb3J0X3Jlc3VsdHNfbGlzdCA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbmxldmVscyhlZGdlX2xpc3RfZGZfd3RheGEkc3R1ZHkpKQp0YXhvX2xldmVsX3NlbGVjdGlvbiA8LSAiZmFtaWx5Igpmb3IoaSBpbiBzZXFfYWxvbmcobGV2ZWxzKGVkZ2VfbGlzdF9kZl93dGF4YSRzdHVkeSkpKXsKICBpbnB1dF9kZl9zdHVkeSA8LSBlZGdlX2xpc3RfZGZfd3RheGEgJT4lIGRwbHlyOjpmaWx0ZXIoc3R1ZHkgPT0gbGV2ZWxzKHN0dWR5KVtpXSkgJT4lCiAgICBtdXRhdGUobWV0aG9kID0gYXMuZmFjdG9yKG1ldGhvZCkpCiAgaW5uZXJfcmVzdWx0X2xpc3QgPC0gdmVjdG9yKG1vZGUgPSAibGlzdCIsIGxlbmd0aCA9IG5sZXZlbHMoaW5wdXRfZGZfc3R1ZHkkbWV0aG9kKSkKICBmb3IoaiBpbiBzZXFfYWxvbmcobGV2ZWxzKGlucHV0X2RmX3N0dWR5JG1ldGhvZCkpKXsKICAgIGlucHV0X2RmX21ldGhvZCA8LSBpbnB1dF9kZl9zdHVkeSAlPiUgCiAgICAgIG11dGF0ZShtZXRob2QgPSBhcy5mYWN0b3IobWV0aG9kKSkgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIobWV0aG9kID09IGxldmVscyhtZXRob2QpW2pdKQogICAgaW5uZXJfcmVzdWx0X2xpc3RbW2pdXTwtdHJ5KG9kZHNfcmF0aW8oaW5wdXRfZGZfbWV0aG9kLCB0YXhvX2xldmVsID0gdGF4b19sZXZlbF9zZWxlY3Rpb24pKQogICAgbmFtZXMoaW5uZXJfcmVzdWx0X2xpc3QpW2pdPC0gbGV2ZWxzKGlucHV0X2RmX21ldGhvZCRtZXRob2QpW2pdCiAgfQogIGFzc29ydF9yZXN1bHRzX2xpc3RbW2ldXSA8LSBpbm5lcl9yZXN1bHRfbGlzdAogIG5hbWVzKGFzc29ydF9yZXN1bHRzX2xpc3QpW2ldIDwtIGxldmVscyhlZGdlX2xpc3RfZGZfd3RheGEkc3R1ZHkpW2ldCn0KIyBjbGVhbi11cCB0aGUgbGlzdCBhbmQgcHV0IHRvZ2V0aGVyIHRoZSBkYXRhIGZyYW1lcwoKZGZfbGlzdCA8LSB2ZWN0b3IobW9kZSA9ICJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKGFzc29ydF9yZXN1bHRzX2xpc3QpKQpmb3IoaSBpbiBzZXFfYWxvbmcoYXNzb3J0X3Jlc3VsdHNfbGlzdCkpewogIGRmX2xpc3RbW2ldXSA8LSBkZl9yZXR1cm4oYXNzb3J0X3Jlc3VsdHNfbGlzdFtbaV1dKSAKICBuYW1lcyhkZl9saXN0KVtpXTwtbmFtZXMoYXNzb3J0X3Jlc3VsdHNfbGlzdClbaV0KfQoKYXNzb3J0X3Jlc3VsdHNfZGYgPC0gYmluZF9yb3dzKGRmX2xpc3QsIC5pZCA9ICJzdHVkeSIpCgojIGNyZWF0ZSBkdW1teSBPUmVzdCB2YWx1ZXMgYnkgcmVwbGFjaW5nIEluZgphc3NvcnRfcmVzdWx0c19kZiA8LSBhc3NvcnRfcmVzdWx0c19kZiAlPiUKICBtdXRhdGUoT1JfZXN0X3dkdW1teSA9IGlmX2Vsc2UoT1JfZXN0PT1JbmYsIDk5LCBPUl9lc3QpKSAlPiUKICBtdXRhdGUobE9SX2VzdF93ZHVtbXkgPSBsb2coT1JfZXN0X3dkdW1teSksCiAgICAgICAgIHNpZ25pZmljYW50ID0gaWZfZWxzZShPUl9wLnZhbHVlIDw9MC4wNSwgVCwgRikpIAoKIyBwbG90LCBzdHVkaWVzIGFyZSBwb29sZWQKZ2dwbG90KGFzc29ydF9yZXN1bHRzX2RmLCBtYXBwaW5nID0gYWVzKHggPSBhc3NvcnRfdGVzdCwgeSA9IGxPUl9lc3Rfd2R1bW15LCBjb2xvdXIgPSBzaWduaWZpY2FudCkpICsKICBmYWNldF93cmFwKH5tZXRob2QpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMikgKwogIGxhYnMoeSA9ICJvZGRzIHJhdGlvIikKCnJtKGFzc29ydF9yZXN1bHRzX2xpc3QsIGlucHV0X2RmX3N0dWR5LCBpbm5lcl9yZXN1bHRfbGlzdCwgaW5wdXRfZGZfbWV0aG9kLCBpLCBqLCBkZl9saXN0KQoKaWYocGxheV9hdWRpbykgYmVlcChzb3VuZCA9IDYpCmlmKGtlZXBfdGltZSkgdG9jKCkKCmBgYApFdmVuIGlmIHRoZSBvZGRzIHJhdGlvcyBhcmUgdmVyeSBoaWdoIGluIHNvbWUgY2FzZXMgKGFjdHVhbGx5IEluZiwgZHVlIHRvIGRpdmlzaW9uIGJ5IDApIHRoZXkgYXJlIG5vdCBzaWduaWZpY2FudC4gTXkgY29uY2x1c2lvbiBpcyB0aGF0IHRoZXJlIGlzIG5vdCBzdWZmaWNpZW50IHN1cHBvcnQgZm9yIHRoZSBvY2N1cnJlbmNlIG9mIHRheG9ub21pYyBhc3NvcnRhdGl2aXR5IChpLmUuIGZvciBhIHNpZ25pZmljYW50bHkgaGlnaGVyIHByb2JhYmlsaXR5IG9mIGhhdmluZyBhIGNvcHJlc2VuY2UgbGluayBiZXR3ZWVuIG1lbWJlcnMgb2YgdGhlIHNhbWUgZmFtaWx5KS4gIApTaW1pbGFyIHJlc3VsdHMgY2FuIGJlIG9idGFpbmVkIHdoZW4gbG9va2luZyBhdCB0aGUgb3JkZXIgYW5kIGNsYXNzIGxldmVsLiAgCgoKIyBDaXRhdGlvbnMgYW5kIGNvcHlyaWdodC4gIAoKIyMgQ2l0YXRpb25zLiAgCgpDaXRhdGlvbnMgZm9yIHRoZSBtZXRhdGF4b25vbWljIHN0dWRpZXMgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIGFyZSBpbiB0aGUgc3R1ZHlfbWV0YWRhdGEgZGF0YSBmcmFtZSAoaWYgYW55KS4gVGhlIG1haW4gcGFja2FnZSB1c2VkIGluIHRoaXMgYW5hbHlzaXMgaXMgTmV0Q29NaSwgYW5kIG9mIGNvdXJzZSwgUGh5bG9zZXEuIENpdGF0aW9ucyBmb3IgYWxsIHBhY2thZ2VzIGFyZSBiZWxvdy4KCmBgYHtyIGNpdGF0aW9uc30KCmFsbF9wYWNrYWdlcyA8LSBjKCJiYXNlIiwgLmNyYW5fcGFja2FnZXMsIC5iaW9jX3BhY2thZ2VzLCAuZ2l0aHViX3BhY2thZ2VzKQptYXAoYWxsX3BhY2thZ2VzLCBjaXRhdGlvbikKCiMgZm9yIHJlcHJvZHVjaWJpbGl0eSB5b3Ugc2hvdWxkIGFsc28gcnVuCiMgc2Vzc2lvbkluZm8oKQoKCgpgYGAKCiMjIENvcHlyaWdodCBub3RpY2UuICAKClNjcmlwdCBjcmVhdGVkIGJ5IEV1Z2VuaW8gUGFyZW50ZSAoZXVnZW5pby5wYXJlbnRlQHVuaWJhcy5pdCksIFVuaXZlcnNpdMOgIGRlZ2xpIFN0dWRpIGRlbGxhIEJhc2lsaWNhdGEsIDIwMjEKaHR0cHM6Ly9naXRodWIuY29tL2VwMTQyClRoaXMgc2NyaXB0IGhhcyBiZWVuIHRlc3RlZCB3aXRoIFIgNC4wLjUgYW5kIDQuMSBvbiB0d28gZGlmZmVyZW50IEFwcGxlIGNvbXB1dGVycyB3aXRoIDggR0IgUkFNIHJ1bm5pbmcgTWFjT1MgMTAuMTQuNi4gIApQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5IG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcyBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOiAgClRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS4K